////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001.
// -------------------------------------------------------------------------
//  File name:   ShapeObject.cpp
//  Version:     v1.00
//  Created:     10/10/2001 by Timur.
//  Compilers:   Visual C++ 6.0
//  Description: CShapeObject implementation.
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "ShapeObject.h"
#include "..\ShapePanel.h"
#include "..\Viewport.h"
#include "Objects/AIWave.h"
#include "Objects/Group.h"
#include "Brush/Brush.h"
#include "Util\Triangulate.h"
#include "AI\AIManager.h"

#include <I3DEngine.h>
#include <IAISystem.h>

#include <vector>
#include "IEntitySystem.h"
#include "EntityPanel.h"

#include "GameEngine.h"
#include "AI\NavDataGeneration\Navigation.h"
#include "PropertiesPanel.h"

#include "HyperGraph/FlowGraph.h"
#include "HyperGraph/FlowGraphManager.h"

#include "SegmentedWorld/SegmentedWorldManager.h"

static CNavigation * GetNavigation ()
{
	return GetIEditor()->GetGameEngine()->GetNavigation();
}

static CGraph * GetGraph ()
{
	return GetNavigation()->GetGraph();
}

//////////////////////////////////////////////////////////////////////////
// CBase implementation.
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNCREATE(CShapeObject,CEntityObject)
IMPLEMENT_DYNCREATE(CLightShapeObject,CShapeObject)
IMPLEMENT_DYNCREATE(CAIForbiddenAreaObject,CShapeObject)
IMPLEMENT_DYNCREATE(CAIForbiddenBoundaryObject,CShapeObject)
IMPLEMENT_DYNCREATE(CAIPathObject,CShapeObject)
IMPLEMENT_DYNCREATE(CAIShapeObject,CShapeObject)
IMPLEMENT_DYNCREATE(CAINavigationModifierObject,CShapeObject)
IMPLEMENT_DYNCREATE(CAIOcclusionPlaneObject,CShapeObject)
IMPLEMENT_DYNCREATE(CAIPerceptionModifierObject, CShapeObject)
IMPLEMENT_DYNCREATE(CAITerritoryObject, CShapeObject)
IMPLEMENT_DYNCREATE(CNavigationAreaObject, CShapeObject)

#define RAY_DISTANCE 100000.0f

//////////////////////////////////////////////////////////////////////////
int CShapeObject::m_rollupId														= 0;
CShapePanel* CShapeObject::m_panel											= 0;
int CShapeObject::m_rollupMultyId												= 0;
CShapeMultySelPanel* CShapeObject::m_panelMulty					= 0;
CAxisHelper CShapeObject::m_selectedPointAxis;
CPropertiesPanel* CShapeObject::m_pSoundPropertiesPanel = NULL;
int CShapeObject::m_nSoundPanelID												= 0;
CVarBlockPtr CShapeObject::m_pSoundPanelVarBlock				= NULL;

//////////////////////////////////////////////////////////////////////////
CShapeObject::CShapeObject()
{
	m_useAxisHelper = false;
	m_bForce2D = false;
	m_bNoCulling = false;
	mv_closed = true;

	mv_areaId = 0;
	
	mv_groupId = 0;
	mv_priority = 0;
	mv_width = 0.0f;
	mv_height = 0.0f;
	mv_displayFilled = false;
	mv_displaySoundInfo = false;

	mv_agentHeight = 1.8f;
	mv_agentWidth = 0.5f;
	mv_VoxelOffsetX = 0.0f;
	mv_VoxelOffsetY = 0.0f;
	mv_renderVoxelGrid = false;
	
	m_bbox.min = m_bbox.max = Vec3(0,0,0);
	m_selectedPoint = -1;
	m_lowestHeight = 0;
	m_bIgnoreGameUpdate = true;
	m_bAreaModified = true;
	m_bDisplayFilledWhenSelected = true;
	m_entityClass = "AreaShape";
	m_bPerVertexHeight = false;

	m_numSplitPoints = 0;
	m_mergeIndex = -1;

	m_updateSucceed = true;

	SetColor( Vec2Rgb(Vec3(0,0.8f,1)) );
	UseMaterialLayersMask(false);

	if(!m_pSoundPanelVarBlock) // Static
		m_pSoundPanelVarBlock = new CVarBlock;
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::Done()
{
	m_entities.Clear();
	m_points.clear();
	UpdateGameArea(true);
	__super::Done();
}

//////////////////////////////////////////////////////////////////////////
bool CShapeObject::Init( IEditor *ie,CBaseObject *prev,const CString &file )
{
	m_bIgnoreGameUpdate = true;

	bool res = __super::Init( ie,prev,file );

	if (prev)
	{
		m_points = ((CShapeObject*)prev)->m_points;
		m_bIgnoreGameUpdate = false;
		mv_closed = ((CShapeObject*)prev)->mv_closed;
		m_abObstructSound	= ((CShapeObject*)prev)->m_abObstructSound;
		CalcBBox();
	}

	m_bIgnoreGameUpdate = false;

	return res;
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::InitVariables()
{
	AddVariable( mv_width,"Width",functor(*this,&CShapeObject::OnShapeChange));
	AddVariable( mv_height,"Height",functor(*this,&CShapeObject::OnShapeChange));
	AddVariable( mv_areaId,"AreaId",functor(*this,&CShapeObject::OnShapeChange));
	AddVariable( mv_groupId,"GroupId",functor(*this,&CShapeObject::OnShapeChange));
	AddVariable( mv_priority,"Priority",functor(*this,&CShapeObject::OnShapeChange));
	AddVariable( mv_closed,"Closed",functor(*this,&CShapeObject::OnShapeChange));
	AddVariable( mv_displayFilled,"DisplayFilled",functor(*this,&CShapeObject::OnShapeChange));
	AddVariable( mv_displaySoundInfo,"DisplaySoundInfo", functor(*this, &CShapeObject::OnSoundParamsChange));
	AddVariable( mv_agentHeight,"Agent_height",functor(*this,&CShapeObject::OnShapeChange));
	AddVariable( mv_agentWidth,"Agent_width",functor(*this,&CShapeObject::OnShapeChange));
	AddVariable( mv_renderVoxelGrid,"Render_voxel_grid",functor(*this,&CShapeObject::OnShapeChange));
	AddVariable( mv_VoxelOffsetX,"voxel_offset_x",functor(*this,&CShapeObject::OnShapeChange));
	AddVariable( mv_VoxelOffsetY,"voxel_offset_y",functor(*this,&CShapeObject::OnShapeChange));
}
//////////////////////////////////////////////////////////////////////////
void CShapeObject::SetName( const CString &name )
{
	__super::SetName( name );
	m_bAreaModified = true;

	if (!IsOnlyUpdateOnUnselect() && !m_bIgnoreGameUpdate)
		UpdateGameArea();
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::InvalidateTM( int nWhyFlags )
{
	__super::InvalidateTM(nWhyFlags);
	m_bAreaModified = true;
//	CalcBBox();

	if (nWhyFlags & TM_RESTORE_UNDO) // Can skip updating game object when restoring undo.
		return;

	if (!IsOnlyUpdateOnUnselect() && !m_bIgnoreGameUpdate)
		UpdateGameArea();
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::GetBoundBox( AABB &box )
{
	box.SetTransformedAABB( GetWorldTM(),m_bbox );
	float s = 1.0f;
	box.min -= Vec3(s,s,s);
	box.max += Vec3(s,s,s);
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::GetLocalBounds( AABB &box )
{
	box = m_bbox;
}

//////////////////////////////////////////////////////////////////////////
bool CShapeObject::HitTest( HitContext &hc )
{
	// First check if ray intersect our bounding box.
	float tr = hc.distanceTolerance/2 + SHAPE_CLOSE_DISTANCE;

	// Find intersection of line with zero Z plane.
	float minDist = FLT_MAX;
	Vec3 intPnt(0,0,0);
	//GetNearestEdge( hc.raySrc,hc.rayDir,p1,p2,minDist,intPnt );

	bool bWasIntersection = false;
	Vec3 ip(0,0,0);
	Vec3 rayLineP1 = hc.raySrc;
	Vec3 rayLineP2 = hc.raySrc + hc.rayDir*RAY_DISTANCE;
	const Matrix34 &wtm = GetWorldTM();

	for (int i = 0; i < m_points.size(); i++)
	{
		int j = (i < m_points.size()-1) ? i+1 : 0;

		if (!mv_closed && j == 0 && i != 0)
			continue;

		Vec3 pi = wtm.TransformPoint(m_points[i]);
		Vec3 pj = wtm.TransformPoint(m_points[j]);

		float d = 0;
		if (RayToLineDistance( rayLineP1,rayLineP2,pi,pj,d,ip ))
		{
			if (d < minDist)
			{
				bWasIntersection = true;
				minDist = d;
				intPnt = ip;
			}
		}

		if (mv_height > 0)
		{
			if (RayToLineDistance( rayLineP1,rayLineP2,pi+Vec3(0,0,mv_height),pj+Vec3(0,0,mv_height),d,ip ))
			{
				if (d < minDist)
				{
					bWasIntersection = true;
					minDist = d;
					intPnt = ip;
				}
			}
			if (RayToLineDistance( rayLineP1,rayLineP2,pi,pi+Vec3(0,0,mv_height),d,ip ))
			{
				if (d < minDist)
				{
					bWasIntersection = true;
					minDist = d;
					intPnt = ip;
				}
			}
		}
	}

	float fShapeCloseDistance = SHAPE_CLOSE_DISTANCE*hc.view->GetScreenScaleFactor(intPnt) * 0.01f;

	if (bWasIntersection && minDist < fShapeCloseDistance+hc.distanceTolerance)
	{
		hc.dist = hc.raySrc.GetDistance(intPnt);
		return true;
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::BeginEditParams( IEditor *ie,int flags )
{
	CBaseObject::BeginEditParams( ie,flags );

	if (!m_panel)
	{
		m_panel = new CShapePanel;
		m_panel->Create( CShapePanel::IDD );
		m_rollupId = ie->AddRollUpPage( ROLLUP_OBJECTS,"Shape Parameters",m_panel );
	}
	if (m_panel)
		m_panel->SetShape( this );

	// Make sure to first remove any old sound-obstruction-roll-up-page in case EndEditParams() didn't get called on a previous instance
	if(m_nSoundPanelID != 0)
	{
		// Internally a var block holds "IVariablePtr", on destroy delete is already called
		m_pSoundPanelVarBlock->Clear();

		ie->RemoveRollUpPage(ROLLUP_OBJECTS, m_nSoundPanelID);
		m_nSoundPanelID = 0;
		m_pSoundPropertiesPanel	= new CPropertiesPanel(AfxGetMainWnd());
	}
	else if(!m_pSoundPropertiesPanel) // Static
		m_pSoundPropertiesPanel	= new CPropertiesPanel(AfxGetMainWnd());

	if(!m_bIgnoreGameUpdate)
		UpdateSoundPanelParams();
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::EndEditParams( IEditor *ie )
{
	if (m_rollupId != 0)
		ie->RemoveRollUpPage( ROLLUP_OBJECTS,m_rollupId );

	m_rollupId			= 0;
	m_panel					= 0;

	if(m_nSoundPanelID != 0)
	{
		// Internally a var block holds "IVariablePtr", on destroy delete is already called
		m_pSoundPanelVarBlock->Clear();

		ie->RemoveRollUpPage(ROLLUP_OBJECTS, m_nSoundPanelID);
		m_nSoundPanelID					= 0;
		m_pSoundPropertiesPanel	= NULL;
	}

	CalcBBox();

	__super::EndEditParams( ie );
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::BeginEditMultiSelParams( bool bAllOfSameType )
{
	__super::BeginEditMultiSelParams( bAllOfSameType );
	if(!bAllOfSameType)
		return;

	if (!m_panelMulty)
	{
		m_panelMulty = new CShapeMultySelPanel;
		m_rollupMultyId = GetIEditor()->AddRollUpPage( ROLLUP_OBJECTS,"Multi Shape Operation", m_panelMulty );
	}
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::EndEditMultiSelParams()
{
	if (m_rollupMultyId != 0)
	{
		GetIEditor()->RemoveRollUpPage( ROLLUP_OBJECTS,m_rollupMultyId );
		CalcBBox();
	}
	m_rollupMultyId = 0;
	m_panelMulty = 0;

	__super::EndEditMultiSelParams();
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::EnableEditParams(bool bEnable)
{
	CEntityObject::EnableEditParams(bEnable);
	if (m_panel)
		m_panel->SetEnable(bEnable);
	if (m_pSoundPropertiesPanel)
		m_pSoundPropertiesPanel->SetEnable(bEnable);
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::EnableEditMultiSelParams(bool bEnable, bool bAllOfSameType)
{
	CEntityObject::EnableEditMultiSelParams(bEnable, bAllOfSameType);
	if (m_panelMulty)
		m_panelMulty->SetEnableMultiSel(bEnable, bAllOfSameType);

}

//////////////////////////////////////////////////////////////////////////
int CShapeObject::MouseCreateCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags )
{
	if (event == eMouseMove || event == eMouseLDown || event == eMouseLDblClick)
	{
		Vec3 pos = view->MapViewToCP(point);

		bool firstTime = false;
		if (m_points.size() < 2)
		{
			SetPos( pos );
		}

		pos.z += GetShapeZOffset();
		
		if (m_points.size() == 0)
		{
			InsertPoint( -1,Vec3(0,0,0),false );
			firstTime = true;
		}
		else
		{
			SetPoint( m_points.size()-1,pos - GetWorldPos() );
		}

		if (event == eMouseLDblClick)
		{
			SW_TEST_OBJ_PLACETO_MCB(GetPos(), GetLayer(), true);
			if (m_points.size() > GetMinPoints())
			{
				m_points.pop_back(); // Remove last unneeded point.
				m_abObstructSound.pop_back();	// Same with the "side sound obstruction list"
				EndCreation();
				SW_ON_OBJ_NEW(this);
				return MOUSECREATE_OK;
			}
			else
				return MOUSECREATE_ABORT;
		}

		if (event == eMouseLDown)
		{
			SW_TEST_OBJ_PLACETO_MCB(GetPos(), GetLayer(), false);
			Vec3 vlen = Vec3(pos.x,pos.y,0) - Vec3(GetWorldPos().x,GetWorldPos().y,0);
			/* Disable that for now.
			if (m_points.size() > 2 && vlen.GetLength() < SHAPE_CLOSE_DISTANCE)
			{
				EndCreation();
				return MOUSECREATE_OK;
			}
			*/
			if (GetPointCount() >= GetMaxPoints())
			{
				SW_TEST_OBJ_PLACETO_MCB(GetPos(), GetLayer(), true);
				EndCreation();
				SW_ON_OBJ_NEW(this);
				return MOUSECREATE_OK;
			}

			InsertPoint( -1,pos-GetWorldPos(),false );

			if (!IsOnlyUpdateOnUnselect())
				UpdateGameArea();
		}
		return MOUSECREATE_CONTINUE;
	}
	return __super::MouseCreateCallback( view,event,point,flags );
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::EndCreation()
{
	SetClosed(mv_closed);
	if (m_panel)
		m_panel->SetShape(this);
}

void CShapeObject::Display( DisplayContext &dc )
{
	if (mv_displaySoundInfo)
	{
		DisplaySoundInfo(dc);
	}
	else
	{
		DisplayNormal(dc);
	}
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::DisplayNormal( DisplayContext &dc )
{
	//dc.renderer->EnableDepthTest(false);

	const Matrix34 &wtm = GetWorldTM();
	COLORREF col = 0;

	float fPointSize = 0.5f;
	if(!IsSelected())
		fPointSize *= 0.5f;

	bool bPointSelected = false;
	if (m_selectedPoint >= 0 && m_selectedPoint < m_points.size())
	{
		bPointSelected = true;
	}

	bool bLineWidth = 0;

	if (!m_updateSucceed)
	{
		// Draw Error in update
		dc.SetColor( GetColor() );
		dc.DrawTextLabel(wtm.GetTranslation(), 2.0f, "Error!", true);
		CString msg("\n\n");
		msg += GetName();
		msg += " (see log)";
		dc.DrawTextLabel(wtm.GetTranslation(), 1.2f, msg, true);
	}

	if (m_points.size() > 1)
	{
		if (IsSelected())
		{
			col = dc.GetSelectedColor();
			dc.SetColor( col );
		}
		else if (IsHighlighted() && !bPointSelected)
		{
			dc.SetColor( RGB(255,120,0) );
			dc.SetLineWidth(3);
			bLineWidth = true;
		}
		else
		{
			if (IsFrozen())
				dc.SetFreezeColor();
			else
				dc.SetColor( GetColor() );
			col = GetColor();
		}
		dc.SetAlpha( 0.8f );

		int num = m_points.size();
		if (num < GetMinPoints())
			num = 1;
		for (int i = 0; i < num; i++)
		{
			int j = (i < m_points.size()-1) ? i+1 : 0;
			if (!mv_closed && j == 0 && i != 0)
				continue;

			Vec3 p0 = wtm.TransformPoint(m_points[i]);
			Vec3 p1 = wtm.TransformPoint(m_points[j]);

			ColorB colMergedTmp = dc.GetColor();
			if(m_mergeIndex==i)
				dc.SetColor( RGB(255,255,127) );
			dc.DrawLine( p0,p1 );
			dc.SetColor( colMergedTmp );
			//DrawTerrainLine( dc,pos+m_points[i],pos+m_points[j] );

			if (mv_height != 0)
			{
				AABB box;
				box.SetTransformedAABB( GetWorldTM(),m_bbox );
				m_lowestHeight = box.min.z;
				// Draw second capping shape from above.
				float z0 = 0;
				float z1 = 0;
				if (m_bPerVertexHeight)
				{
					z0 = p0.z + mv_height;
					z1 = p1.z + mv_height;
				}
				else
				{
					z0 = m_lowestHeight + mv_height;
					z1 = z0;
				}
				dc.DrawLine( p0,Vec3(p0.x,p0.y,z0) );
				dc.DrawLine( Vec3(p0.x,p0.y,z0),Vec3(p1.x,p1.y,z1) );

				if (mv_displayFilled || (gSettings.viewports.bFillSelectedShapes && IsSelected()) )
				{
					dc.CullOff();
					dc.DepthWriteOff();
					ColorB c = dc.GetColor();
					dc.SetAlpha( 0.3f );
					dc.DrawQuad( p0,Vec3(p0.x,p0.y,z0),Vec3(p1.x,p1.y,z1),p1 );
					dc.CullOn();
					dc.SetColor(c);
					if(!IsHighlighted())
						dc.SetAlpha( 0.8f );
					dc.DepthWriteOn();
				}
			}
		}

		// Draw selected point.
		if (bPointSelected && m_useAxisHelper)
		{
			// The axis is drawn before the points because the points have higher pick priority and should appear on top of the axis.
			Matrix34	axis;
			axis.SetTranslationMat(wtm.TransformPoint(m_points[m_selectedPoint]));
			m_selectedPointAxis.SetMode(CAxisHelper::MOVE_MODE);
			m_selectedPointAxis.DrawAxis(axis, dc);
		}

		// Draw points without depth test to make them editable even behind other objects.
		if(IsSelected())
			dc.DepthTestOff();

		if (IsFrozen())
			col = dc.GetFreezeColor();
		else
			col = GetColor();

		for (int i = 0; i < num; i++)
		{
			Vec3 p0 = wtm.TransformPoint(m_points[i]);
			float fScale = fPointSize*dc.view->GetScreenScaleFactor(p0) * 0.01f;
			Vec3 sz(fScale,fScale,fScale);

			if(bPointSelected && i == m_selectedPoint)
				dc.SetSelectedColor();
			else
				dc.SetColor(col);

			dc.DrawWireBox( p0-sz,p0+sz );
		}

		if(IsSelected())
			dc.DepthTestOn();
	}

	if (!m_entities.IsEmpty())
	{
		Vec3 vcol = Rgb2Vec(col);
		int num = m_entities.GetCount();
		for (int i = 0; i < num; i++)
		{
			CBaseObject *obj = m_entities.Get(i);
			if (!obj)
				continue;
			int p1,p2;
			float dist;
			Vec3 intPnt;
			GetNearestEdge( obj->GetWorldPos(),p1,p2,dist,intPnt );
			dc.DrawLine( intPnt,obj->GetWorldPos(),ColorF(vcol.x,vcol.y,vcol.z,0.7f),ColorF(1,1,1,0.7f) );
		}
	}

	if (mv_closed && !IsFrozen())
	{
		//if (mv_displayFilled) // || (IsSelected() && m_bDisplayFilledWhenSelected) || (IsHighlighted() && m_bDisplayFilledWhenSelected))
		if (mv_displayFilled || (gSettings.viewports.bFillSelectedShapes && IsSelected()))
		{
			if (IsHighlighted())
				dc.SetColor( GetColor(),0.1f );
			else
				dc.SetColor( GetColor(),0.3f );
			static std::vector<Vec3> tris;
			tris.resize(0);
			tris.reserve( m_points.size()*3 );
			if (CTriangulate::Process( m_points,tris ))
			{
				if (m_bNoCulling)
					dc.CullOff();
				for (int i = 0; i < tris.size(); i += 3)
				{
					dc.DrawTri( wtm.TransformPoint(tris[i]),wtm.TransformPoint(tris[i+1]),wtm.TransformPoint(tris[i+2]) );
				}
				if (m_bNoCulling)
					dc.CullOn();
			}
		}
	}

	if(m_numSplitPoints>0)
	{
		COLORREF col = GetColor();
		dc.SetColor( RGB(127,255,127) );
		for(int i = 0; i<m_numSplitPoints; i++)
		{
			Vec3 p0 = wtm.TransformPoint(m_splitPoints[i]);
			float fScale = fPointSize*dc.view->GetScreenScaleFactor(p0) * 0.01f;
			Vec3 sz(fScale,fScale,fScale);
			dc.DrawWireBox( p0-sz,p0+sz );
		}
		if(m_numSplitPoints==2)
		{
			Vec3 p0 = wtm.TransformPoint(m_splitPoints[0]);
			Vec3 p1 = wtm.TransformPoint(m_splitPoints[1]);
			dc.DrawLine( p0, p1);
		}
		dc.SetColor( col );
	}

	if (bLineWidth)
		dc.SetLineWidth(0);

	//dc.renderer->EnableDepthTest(true);

	if (mv_renderVoxelGrid)
	{
		AABB tempbox;
		tempbox.SetTransformedAABB( GetWorldTM(),m_bbox );

		tempbox.min -= 5.0f * Vec3(mv_agentWidth, mv_agentWidth, 0.0f);
		tempbox.max += 5.0f * Vec3(mv_agentWidth, mv_agentWidth, 0.0f);

		float starty = tempbox.min.y + mv_VoxelOffsetY;


		//SAuxGeomRenderFlags renderFlags = gEnv->pRenderer->GetIRenderAuxGeom()->GetRenderFlags();
		//SAuxGeomRenderFlags renderFlagsNew = renderFlags;
		//renderFlagsNew.SetFillMode(e_FillModeWireframe);
		//gEnv->pRenderer->GetIRenderAuxGeom()->SetRenderFlags(renderFlagsNew);
		
		ColorB colVoxel(255, 255, 255);
		float width = (mv_agentWidth > 0.0f) ? mv_agentWidth : 0.5f;

		while (starty < tempbox.max.y)
		{
			float startx = tempbox.min.x + mv_VoxelOffsetX;

			while (startx < tempbox.max.x)
			{			
				gEnv->pRenderer->GetIRenderAuxGeom()->DrawAABB (AABB (Vec3 (startx, starty, tempbox.min.z), Vec3 (startx + mv_agentWidth, starty + mv_agentWidth, tempbox.min.z + mv_height)), false, colVoxel, eBBD_Faceted);

				startx += width;
			}

			starty += width;
		}

		//gEnv->pRenderer->GetIRenderAuxGeom()->SetRenderFlags(renderFlags);

	}

	DrawDefault(dc,GetColor());
}

void CShapeObject::DisplaySoundInfo( DisplayContext &dc )
{

	//dc.renderer->EnableDepthTest(false);

	const Matrix34 &wtm = GetWorldTM();
	COLORREF col = 0;

	ColorB const oObstructionFilled(255, 0, 0, 120);
	ColorB const oObstructionNotFilled(255, 0, 0, 20);
	ColorB const oNoObstructionFilled(0, 255, 0, 120);
	ColorB const oNoObstructionNotFilled(0, 255, 0, 20);

	float fPointSize = 0.5f;
	if(!IsSelected())
		fPointSize *= 0.5f;

	bool bPointSelected = false;
	if (m_selectedPoint >= 0 && m_selectedPoint < m_points.size())
	{
		bPointSelected = true;
	}

	bool bLineWidth = 0;

	if (!m_updateSucceed)
	{
		// Draw Error in update
		dc.SetColor( GetColor() );
		dc.DrawTextLabel(wtm.GetTranslation(), 2.0f, "Error!", true);
		CString msg("\n\n");
		msg += GetName();
		msg += " (see log)";
		dc.DrawTextLabel(wtm.GetTranslation(), 1.2f, msg, true);
	}

	if (m_points.size() > 1)
	{
		if (IsSelected() && mv_height != 0.0f)
		{
			col = dc.GetSelectedColor();
			dc.SetColor( col );
			dc.SetLineWidth(2);
			bLineWidth = true;
		}
		else if (IsHighlighted() && !bPointSelected)
		{
			dc.SetColor( RGB(255,120,0) );
			dc.SetLineWidth(3);
			bLineWidth = true;
		}
		else
		{
			if (IsFrozen())
				dc.SetFreezeColor();
			else
				dc.SetColor( GetColor() );
			col = GetColor();
		}
		dc.SetAlpha( 0.8f );

		int num = m_points.size();
		if (num < GetMinPoints())
			num = 1;
		for (int i = 0; i < num; i++)
		{
			int j = (i < m_points.size()-1) ? i+1 : 0;
			if (!mv_closed && j == 0 && i != 0)
				continue;

			Vec3 p0 = wtm.TransformPoint(m_points[i]);
			Vec3 p1 = wtm.TransformPoint(m_points[j]);

			ColorB colMergedTmp = dc.GetColor();
			if(m_mergeIndex == i)
				dc.SetColor( RGB(255,255,127) );
			else if(mv_height == 0.0f && IsSelected())
			{
				if(m_abObstructSound[i])
					dc.SetColor(oObstructionFilled);
				else
					dc.SetColor(oNoObstructionFilled);
			}

			dc.DrawLine( p0,p1 );
			dc.SetColor( colMergedTmp );
			//DrawTerrainLine( dc,pos+m_points[i],pos+m_points[j] );

			if (mv_height != 0)
			{
				AABB box;
				box.SetTransformedAABB( GetWorldTM(),m_bbox );
				m_lowestHeight = box.min.z;
				// Draw second capping shape from above.
				float z0 = 0;
				float z1 = 0;
				if (m_bPerVertexHeight)
				{
					z0 = p0.z + mv_height;
					z1 = p1.z + mv_height;
				}
				else
				{
					z0 = m_lowestHeight + mv_height;
					z1 = z0;
				}
				dc.DrawLine( p0,Vec3(p0.x,p0.y,z0) );
				dc.DrawLine( Vec3(p0.x,p0.y,z0),Vec3(p1.x,p1.y,z1) );

				// Draw the sides
				if (mv_displayFilled || (gSettings.viewports.bFillSelectedShapes && IsSelected()) )
				{
					dc.CullOff();
					dc.DepthWriteOff();
					//dc.pRenderAuxGeom->GetRenderFlags().SetAlphaBlendMode(e_AlphaAdditive);
					ColorB c = dc.GetColor();

					// Manipulate color on sound obstruction, draw it a little thicker and redder to give this impression
					if(m_abObstructSound[i])
						dc.SetColor(oObstructionFilled);
					else
						dc.SetColor(oNoObstructionFilled);

					dc.DrawQuad( p0,Vec3(p0.x,p0.y,z0),Vec3(p1.x,p1.y,z1),p1 );
					//dc.pRenderAuxGeom->GetRenderFlags().SetAlphaBlendMode(e_AlphaBlended);
					dc.DepthWriteOn();
					dc.CullOn();
					dc.SetColor(c);
				}
				else if(IsSelected())
				{
					// If it's selected but not supposed to be rendered filled, give it slightly an obstructing impression
					dc.CullOff();
					dc.DepthWriteOff();
					ColorB c = dc.GetColor();

					// Manipulate color so it looks a little thicker and redder
					if(m_abObstructSound[i])
						dc.SetColor(oObstructionNotFilled);
					else
						dc.SetColor(oNoObstructionNotFilled);

					dc.DrawQuad(p0, Vec3(p0.x,p0.y,z0), Vec3(p1.x,p1.y,z1), p1);
					dc.DepthWriteOn();
					dc.CullOn();
					dc.SetColor(c);
				}
			}
		}

		// Draw selected point.
		if (bPointSelected && m_useAxisHelper)
		{
			// The axis is drawn before the points because the points have higher pick priority and should appear on top of the axis.
			Matrix34	axis;
			axis.SetTranslationMat(wtm.TransformPoint(m_points[m_selectedPoint]));
			m_selectedPointAxis.SetMode(CAxisHelper::MOVE_MODE);
			m_selectedPointAxis.DrawAxis(axis, dc);
		}

		// Draw points without depth test to make them editable even behind other objects.
		if(IsSelected())
			dc.DepthTestOff();

		if (IsFrozen())
			col = dc.GetFreezeColor();
		else
			col = GetColor();

		for (int i = 0; i < num; i++)
		{
			Vec3 p0 = wtm.TransformPoint(m_points[i]);
			float fScale = fPointSize*dc.view->GetScreenScaleFactor(p0) * 0.01f;
			Vec3 sz(fScale,fScale,fScale);

			if(bPointSelected && i == m_selectedPoint)
				dc.SetSelectedColor();
			else
				dc.SetColor(col);

			dc.DrawWireBox( p0-sz,p0+sz );
		}

		if(IsSelected())
			dc.DepthTestOn();
	}

	if (!m_entities.IsEmpty())
	{
		Vec3 vcol = Rgb2Vec(col);
		int num = m_entities.GetCount();
		for (int i = 0; i < num; i++)
		{
			CBaseObject *obj = m_entities.Get(i);
			if (!obj)
				continue;
			int p1,p2;
			float dist;
			Vec3 intPnt;
			GetNearestEdge( obj->GetWorldPos(),p1,p2,dist,intPnt );
			dc.DrawLine( intPnt,obj->GetWorldPos(),ColorF(vcol.x,vcol.y,vcol.z,0.7f),ColorF(1,1,1,0.7f) );
		}
	}

	if (mv_closed && !IsFrozen() && mv_height > 0.0f)
	{
		//if (mv_displayFilled) // || (IsSelected() && m_bDisplayFilledWhenSelected) || (IsHighlighted() && m_bDisplayFilledWhenSelected))
		if (mv_displayFilled || (gSettings.viewports.bFillSelectedShapes && IsSelected()))
		{
			if (IsHighlighted())
				dc.SetColor( GetColor(),0.1f );
			else
				dc.SetColor( GetColor(),0.3f );

			static std::vector<Vec3> tris;
			tris.resize(0);
			tris.reserve( m_points.size()*3 );
			if (CTriangulate::Process( m_points,tris ))
			{
				// Manipulate color on sound obstruction, draw it a little thicker and redder to give this impression
				//bool bRoofObstructs = false;
				bool bFloorObstructs = false;

				/*if(m_abObstructSound[m_abObstructSound.size()-2])
				bRoofObstructs = true;*/
				if(m_abObstructSound[m_abObstructSound.size()-1])
					bFloorObstructs = true;

				ColorB c = dc.GetColor();
				dc.CullOff();

				//float const fAreaTopPos = m_lowestHeight + mv_height;
				for (int i = 0; i < tris.size(); i += 3)
				{
					if(bFloorObstructs)
						dc.SetColor(oObstructionFilled);
					else
						dc.SetColor(oNoObstructionFilled);

					// Draw the floor
					dc.DrawTri( wtm.TransformPoint(tris[i]),wtm.TransformPoint(tris[i+1]),wtm.TransformPoint(tris[i+2]) );

					// Draw a roof only if it obstructs sounds (temporarily disabled because of too many ugly render artifacts)
					/*if(mv_height != 0 && bRoofObstructs)
					{
					dc.SetColor(oObstructionFilled);

					tris[i]			= wtm.TransformPoint(tris[i]);
					tris[i+1]		= wtm.TransformPoint(tris[i+1]);
					tris[i+2]		= wtm.TransformPoint(tris[i+2]);

					tris[i].z		= fAreaTopPos;
					tris[i+1].z	= fAreaTopPos;
					tris[i+2].z	= fAreaTopPos;

					dc.DrawTri(tris[i], tris[i+1], tris[i+2]);
					}*/
				}

				dc.SetColor(c);
				dc.CullOn();
			}
		}
		else if(IsSelected())
		{
			static std::vector<Vec3> tris;
			tris.resize(0);
			tris.reserve( m_points.size()*3 );
			if (CTriangulate::Process( m_points,tris ))
			{
				// Manipulate color on sound obstruction, draw it a little thicker and redder to give this impression
				//bool bRoofObstructs = false;
				bool bFloorObstructs = false;

				/*if(m_abObstructSound[m_abObstructSound.size()-2])
				bRoofObstructs = true;*/
				if(m_abObstructSound[m_abObstructSound.size()-1])
					bFloorObstructs = true;

				dc.CullOff();
				ColorB c = dc.GetColor();
				dc.SetAlpha(0.0f);

				//float const fAreaTopPos = m_lowestHeight + mv_height;
				for (int i = 0; i < tris.size(); i += 3)
				{
					if(bFloorObstructs)
						dc.SetColor(oObstructionNotFilled);
					else
						dc.SetColor(oNoObstructionNotFilled);

					// Draw the floor
					dc.DrawTri( wtm.TransformPoint(tris[i]),wtm.TransformPoint(tris[i+1]),wtm.TransformPoint(tris[i+2]) );

					// Draw a roof only if it obstructs sounds (temporarily disabled because of too many ugly render artifacts)
					//if(mv_height != 0 && bRoofObstructs)
					//{
					//	dc.SetColor(oObstructionNotFilled);

					//	tris[i]			= wtm.TransformPoint(tris[i]);
					//	tris[i+1]		= wtm.TransformPoint(tris[i+1]);
					//	tris[i+2]		= wtm.TransformPoint(tris[i+2]);

					//	tris[i].z		= fAreaTopPos;
					//	tris[i+1].z	= fAreaTopPos;
					//	tris[i+2].z	= fAreaTopPos;

					//	// Draw the roof
					//	dc.DrawTri(tris[i], tris[i+1], tris[i+2]);
					//}
				}

				dc.SetColor(c);
				dc.CullOn();
			}
		}
	}

	if(m_numSplitPoints>0)
	{
		COLORREF col = GetColor();
		dc.SetColor( RGB(127,255,127) );
		for(int i = 0; i<m_numSplitPoints; i++)
		{
			Vec3 p0 = wtm.TransformPoint(m_splitPoints[i]);
			float fScale = fPointSize*dc.view->GetScreenScaleFactor(p0) * 0.01f;
			Vec3 sz(fScale,fScale,fScale);
			dc.DrawWireBox( p0-sz,p0+sz );
		}
		if(m_numSplitPoints==2)
		{
			Vec3 p0 = wtm.TransformPoint(m_splitPoints[0]);
			Vec3 p1 = wtm.TransformPoint(m_splitPoints[1]);
			dc.DrawLine( p0, p1);
		}
		dc.SetColor( col );
	}

	if (bLineWidth)
		dc.SetLineWidth(0);

	//dc.renderer->EnableDepthTest(true);

	DrawDefault(dc,GetColor());


};
//////////////////////////////////////////////////////////////////////////
void CShapeObject::DrawTerrainLine( DisplayContext &dc,const Vec3 &p1,const Vec3 &p2 )
{
	float len = (p2-p1).GetLength();
	int steps = len/2;
	if (steps <= 1)
	{
		dc.DrawLine( p1,p2 );
		return;
	}
	Vec3 pos1 = p1;
	Vec3 pos2 = p1;
	for (int i = 0; i < steps-1; i++)
	{
		pos2 = p1 + (1.0f/i)*(p2-p1);
		pos2.z = dc.engine->GetTerrainElevation(pos2.x,pos2.y);
		dc.SetColor( i*2,0,0,1 );
		dc.DrawLine( pos1,pos2 );
		pos1 = pos2;
	}
	//dc.DrawLine( pos2,p2 );
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::Serialize( CObjectArchive &ar )
{
	m_bIgnoreGameUpdate = true;
	__super::Serialize( ar );
	m_bIgnoreGameUpdate = false;

	XmlNodeRef xmlNode = ar.node;
	if (ar.bLoading)
	{
		m_bAreaModified = true;
		m_bIgnoreGameUpdate = true;
		m_entities.Clear();

		GUID entityId;
		if (xmlNode->getAttr( "EntityId",entityId ))
		{
			// For Backward compatability.
			//m_entities.push_back(entityId);
			ar.SetResolveCallback( this,entityId,functor(m_entities,&CSafeObjectsArray::Add) );
		}

		// Load Entities.
		m_points.clear();
		XmlNodeRef points = xmlNode->findChild( "Points" );
		if (points)
		{
			for (int i = 0; i < points->getChildCount(); i++)
			{
				XmlNodeRef pnt = points->getChild(i);

				// Get position
				Vec3 pos;
				pnt->getAttr( "Pos",pos );
				m_points.push_back(pos);
			}
		}
		XmlNodeRef entities = xmlNode->findChild( "Entities" );
		if (entities)
		{
			for (int i = 0; i < entities->getChildCount(); i++)
			{
				XmlNodeRef ent = entities->getChild(i);
				GUID entityId;
				if (ent->getAttr( "Id",entityId ))
				{
					//m_entities.push_back(id);
					ar.SetResolveCallback( this,entityId,functor(m_entities,&CSafeObjectsArray::Add) );
				}
			}
		}

		if (ar.bUndo)
		{
			// Update game area only in undo mode.
			m_bIgnoreGameUpdate = false;
		}
		CalcBBox();
		UpdateGameArea();

		// Update UI.
		if (m_panel && m_panel->GetShape() == this)
			m_panel->SetShape(this);
	}
	else
	{
		// Saving.
		// Save Points.
		if (!m_points.empty())
		{
			XmlNodeRef points = xmlNode->newChild( "Points" );
			for (int i = 0; i < m_points.size(); i++)
			{
				XmlNodeRef pnt = points->newChild( "Point" );
				pnt->setAttr( "Pos",m_points[i] );
			}
		}
		// Save Entities.
		if (!m_entities.IsEmpty())
		{
			XmlNodeRef nodes = xmlNode->newChild( "Entities" );
			for (int i = 0; i < m_entities.GetCount(); i++)
			{
				XmlNodeRef entNode = nodes->newChild( "Entity" );
				if (m_entities.Get(i))
					entNode->setAttr( "Id",m_entities.Get(i)->GetId() );
			}
		}
	}

	// Now serialize the sound variables
	if(m_pSoundPanelVarBlock)
	{
		if(ar.bLoading)
		{
			// Create the variables
			// First clear the remains out
			m_pSoundPanelVarBlock->Clear();

			size_t const nPointsCount = m_points.size();
			for(size_t i = 0; i < nPointsCount; ++i)
			{
				CVariable<bool>* const pvTemp = new CVariable<bool>;

				stack_string cTemp;
				cTemp.Format("Side%d", i+1);

				// And add each to the block
				m_pSoundPanelVarBlock->AddVariable(pvTemp, cTemp);

				// If we're at the last point add the last 2 "Roof" and "Floor"
				if(i == nPointsCount-1)
				{
					CVariable<bool>* const __restrict pvTempRoof		= new CVariable<bool>;
					CVariable<bool>* const __restrict pvTempFloor		= new CVariable<bool>;
					m_pSoundPanelVarBlock->AddVariable(pvTempRoof, "Roof");
					m_pSoundPanelVarBlock->AddVariable(pvTempFloor, "Floor");
				}
			}

			// Now read in the data
			XmlNodeRef pSoundDataNode = xmlNode->findChild("SoundData");
			if(pSoundDataNode)
			{
				// Make sure we stay backwards compatible and also find the old "Side:" formatting
				// This can go out once every level got saved with the new formatting
				char const* const pcTemp = pSoundDataNode->getAttr("Side:1");
				if (pcTemp && pcTemp[0])
				{
					// We have the old formatting
					m_pSoundPanelVarBlock->Clear();

					for(size_t i = 0; i < nPointsCount; ++i)
					{
						CVariable<bool>* const pvTempOld = new CVariable<bool>;

						stack_string cTempOld;
						cTempOld.Format("Side:%d", i+1);

						// And add each to the block
						m_pSoundPanelVarBlock->AddVariable(pvTempOld, cTempOld);

						// If we're at the last point add the last 2 "Roof" and "Floor"
						if(i == nPointsCount-1)
						{
							CVariable<bool>* const __restrict pvTempRoofOld		= new CVariable<bool>;
							CVariable<bool>* const __restrict pvTempFloorOld	= new CVariable<bool>;
							m_pSoundPanelVarBlock->AddVariable(pvTempRoofOld, "Roof");
							m_pSoundPanelVarBlock->AddVariable(pvTempFloorOld, "Floor");
						}
					}
				}

				m_pSoundPanelVarBlock->Serialize(pSoundDataNode, true);
			}

			// Copy the data to the "bools" list
			// First clear the remains out
			m_abObstructSound.clear();

			size_t const nVarCount = m_pSoundPanelVarBlock->GetVarsCount();
			for(size_t i = 0; i < nVarCount; ++i)
			{
				IVariable const* const pTemp = m_pSoundPanelVarBlock->GetVariable(i);
				if(pTemp)
				{
					bool bTemp = false;
					pTemp->Get(bTemp);
					m_abObstructSound.push_back(bTemp);

					if(i >= nVarCount-2)
					{
						// Here check for backwards compatibility reasons if "ObstructRoof" and "ObstructFloor" still exists
						bool bTemp = false;
						if(i == nVarCount-2)
						{
							if(xmlNode->getAttr("ObstructRoof", bTemp))
								m_abObstructSound[i] = bTemp;
						}
						else
						{
							if(xmlNode->getAttr("ObstructFloor", bTemp))
								m_abObstructSound[i] = bTemp;
						}
					}
				}
			}

			// Since the var block is a static object clear it for the next object to be empty
			m_pSoundPanelVarBlock->Clear();
		}
		else
		{
			XmlNodeRef pSoundDataNode = xmlNode->newChild("SoundData");
			if(pSoundDataNode)
			{
				// First clear the remains out
				m_pSoundPanelVarBlock->Clear();

				// Create the variables
				size_t const nCount = m_abObstructSound.size();
				for(size_t i = 0; i < nCount; ++i)
				{
					CVariable<bool>* const pvTemp = new CVariable<bool>;
					pvTemp->Set(m_abObstructSound[i]);
					stack_string cTemp;

					if(i == nCount-2)				// Next to last one == "Roof"
						cTemp.Format("Roof");
					else if(i == nCount-1)	// Last one == "Floor"
						cTemp.Format("Floor");
					else
						cTemp.Format("Side%d", i+1);

					// And add each to the block
					m_pSoundPanelVarBlock->AddVariable(pvTemp, cTemp);
				}

				m_pSoundPanelVarBlock->Serialize(pSoundDataNode, false);
				m_pSoundPanelVarBlock->Clear();
			}
		}
	}

	m_bIgnoreGameUpdate = false;
}

//////////////////////////////////////////////////////////////////////////
XmlNodeRef CShapeObject::Export( const CString &levelPath,XmlNodeRef &xmlNode )
{
	XmlNodeRef objNode = __super::Export( levelPath,xmlNode );
	return objNode;
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::CalcBBox()
{
	if (m_points.empty())
		return;

	// Reposition shape, so that shape object position is in the middle of the shape.
	Vec3 center = m_points[0];
	if (center.x != 0 || center.y != 0 || center.z != 0)
	{
		Matrix34 ltm = GetLocalTM();

		Vec3 newPos = GetPos() + ltm.TransformVector(center);
		SW_TEST_OBJ_MOVETO(this, newPos, GetLayer(), false);

		// May not work correctly if shape is transformed.
		for (int i = 0; i < m_points.size(); i++)
		{
			m_points[i] -= center;
		}
		SetPos( GetPos() + ltm.TransformVector(center) );
	}

	// First point must always be 0,0,0.
	m_bbox.Reset();
	for (int i = 0; i < m_points.size(); i++)
	{
		m_bbox.Add( m_points[i] );
	}
	if (m_bbox.min.x > m_bbox.max.x)
	{
		m_bbox.min = m_bbox.max = Vec3(0,0,0);
	}
	if(mv_height)
	{
		AABB box;
		box.SetTransformedAABB( GetWorldTM(),m_bbox );
		m_lowestHeight = box.min.z;

		if (m_bPerVertexHeight)
			m_bbox.max.z += mv_height;
		else
		{
			//m_bbox.max.z = max( m_bbox.max.z,(float)(m_lowestHeight+mv_height) );
			box.max.z = max( box.max.z,(float)(m_lowestHeight+mv_height) );
			Matrix34 mat=GetWorldTM().GetInverted();
			Vec3 p = mat.TransformPoint(box.max);
			m_bbox.max.z = max( m_bbox.max.z, p.z );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::SetSelected( bool bSelect )
{
	bool bWasSelected = IsSelected();
	__super::SetSelected( bSelect );
	if (!bSelect && bWasSelected)
	{
		// When unselecting shape, update area in game.
		if (m_bAreaModified)
			UpdateGameArea();
		m_bAreaModified = false;
	}
	m_mergeIndex = -1;
}

//////////////////////////////////////////////////////////////////////////
int CShapeObject::InsertPoint( int index,const Vec3 &point, bool const bModifying )
{
	if (GetPointCount() >= GetMaxPoints())
		return GetPointCount()-1;

	// Make sure we don't have points too close to each other
	size_t nCount	= m_points.size();

	if (nCount > 1) // At least 1 point should be set already
	{
		// Don't compare to the last entry if index is smaller 0,
		// this indicates that we're creating a new shape and not editing it and just adding points.
		// When creating a new shape the new point is already pushed to the end of the vector.
		nCount -= (bModifying?0:1);
		for (size_t nIdx = 0; nIdx < nCount; ++nIdx)
		{
			float const fDiffX = cry_fabsf(m_points[nIdx].x - point.x);
			float const fDiffY = cry_fabsf(m_points[nIdx].y - point.y);
			float const fDiffZ = cry_fabsf(m_points[nIdx].z - point.z);

			if (fDiffX < SHAPE_POINT_MIN_DISTANCE && fDiffY < SHAPE_POINT_MIN_DISTANCE && fDiffZ < SHAPE_POINT_MIN_DISTANCE)
			{
				CRY_ASSERT_MESSAGE(fDiffX > SHAPE_POINT_MIN_DISTANCE && fDiffY > SHAPE_POINT_MIN_DISTANCE && fDiffZ > SHAPE_POINT_MIN_DISTANCE, "The point is too close to another point!");
				return nIdx;
			}
		}
	}

	StoreUndo( "Insert Point" );

	int nNewIndex		= -1;
	m_bAreaModified	= true;

	if (index < 0 || index >= m_points.size())
	{
		m_points.push_back( point );
		nNewIndex = m_points.size()-1;

		if(m_abObstructSound.empty())
		{
			// This shape is being created therefore add the point plus roof and floor positions
			m_abObstructSound.resize(3);
		}
		else
		{
			// Update the "bools list", insert just before the "Roof" position
			m_abObstructSound.insert(m_abObstructSound.end()-2, false);
		}
	}
	else
	{
		m_points.insert( m_points.begin()+index,point );
		nNewIndex = index;

		// Also update the "bools list"
		m_abObstructSound.insert(m_abObstructSound.begin()+index, false);
	}
	SetPoint( nNewIndex,point );
	CalcBBox();

	return nNewIndex;
}

//////////////////////////////////////////////////////////////////////////
int CShapeObject::SetSplitPoint(int index, const Vec3 &point, int numPoint)
{
	if(numPoint>1)
		return -1;
	if(numPoint<0)
	{
		m_numSplitPoints = 0;
		return -1;
	}

	if(numPoint==1 && index==m_splitIndicies[0])
		return 1;

	if(index!=-2)// if index==-2 need change only m_numSplitPoints
	{
		m_splitIndicies[numPoint] = index;
		m_splitPoints[numPoint] = point;
	}
	m_numSplitPoints = numPoint+1;
	return m_numSplitPoints;
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::Split()
{
	if(m_numSplitPoints!=2)
		return;

	int in0 = InsertPoint(m_splitIndicies[0], m_splitPoints[0], true);
	if(m_splitIndicies[0]!=-1 && m_splitIndicies[1]!=-1 && m_splitIndicies[1]>m_splitIndicies[0])
		m_splitIndicies[1]++;
	int in1 = InsertPoint(m_splitIndicies[1], m_splitPoints[1], true);

	if(in1<=in0)
	{
		in0++;
		int vs=in1;
		in1 = in0;
		in0 = vs;
	}

	CShapeObject * pNewShape = (CShapeObject *)GetObjectManager()->CloneObject(this);
	int i;
	for(i = in0+1; i<in1; i++)
		RemovePoint(in0+1);
	if(pNewShape)
	{
		int cnt = pNewShape->GetPointCount();
		for(i = in1+1; i<cnt; i++)
			pNewShape->RemovePoint(in1+1);
		for(i = 0; i<in0; i++)
			pNewShape->RemovePoint(0);
	}

	m_bAreaModified = true;
	if (!IsOnlyUpdateOnUnselect())
		UpdateGameArea();

	SW_ON_OBJ_MOD(this);
	SW_ON_OBJ_NEW(pNewShape);
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::SetMergeIndex( int index )
{
	m_mergeIndex = index;
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::Merge( CShapeObject * shape )
{
	if(!shape || m_mergeIndex<0 ||  shape->m_mergeIndex<0)
		return;

	SW_ON_OBJ_MOD(this);
	SW_ON_OBJ_DEL(shape);

	const Matrix34 &tm = GetWorldTM();
	const Matrix34 &shapeTM = shape->GetWorldTM();

	int index = m_mergeIndex+1;

	Vec3 p0 = tm.TransformPoint(GetPoint(m_mergeIndex));
	Vec3 p1 = tm.TransformPoint(GetPoint(m_mergeIndex+1<GetPointCount()? m_mergeIndex+1 : 0));

	Vec3 p2 = shapeTM.TransformPoint(shape->GetPoint(shape->m_mergeIndex));
	Vec3 p3 = shapeTM.TransformPoint(shape->GetPoint(shape->m_mergeIndex+1<shape->GetPointCount()? shape->m_mergeIndex+1 : 0));

	float sum1 = p0.GetDistance(p2) + p1.GetDistance(p3);
	float sum2 = p0.GetDistance(p3) + p1.GetDistance(p2);

	if(sum2<sum1)
	{
		shape->ReverseShape();
		shape->m_mergeIndex = shape->GetPointCount()-1-shape->m_mergeIndex;
	}

	int i;
	for(i=shape->m_mergeIndex+1; i<shape->GetPointCount(); i++)
	{
		Vec3 pnt = shapeTM.TransformPoint(shape->GetPoint(i));
		pnt = tm.GetInverted().TransformPoint(pnt);
		InsertPoint(index , pnt, true);
	}
	for(i=0; i<=shape->m_mergeIndex; i++)
	{
		Vec3 pnt = shapeTM.TransformPoint(shape->GetPoint(i));
		pnt = tm.GetInverted().TransformPoint(pnt);
		InsertPoint(index , pnt, true);
	}

	shape->SetMergeIndex(-1);
	GetObjectManager()->DeleteObject(shape);

	m_bAreaModified = true;
	if (!IsOnlyUpdateOnUnselect())
		UpdateGameArea();
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::RemovePoint( int index )
{
	if ((index >= 0 || index < m_points.size()) && m_points.size() > GetMinPoints())
	{
		m_bAreaModified = true;
		StoreUndo( "Remove Point" );
		m_points.erase( m_points.begin()+index );
		m_abObstructSound.erase( m_abObstructSound.begin()+index );
		CalcBBox();

		m_bAreaModified = true;
		if (!IsOnlyUpdateOnUnselect())
			UpdateGameArea();
	}
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::PostClone( CBaseObject *pFromObject,CObjectCloneContext &ctx )
{
	__super::PostClone( pFromObject,ctx );

	CShapeObject *pFromShape = (CShapeObject*)pFromObject;
	// Clone event targets.
	if (!pFromShape->m_entities.IsEmpty())
	{
		int numEntities = pFromShape->m_entities.GetCount();
		for (int i = 0; i < numEntities; i++)
		{
			CBaseObject *pTarget = pFromShape->m_entities.Get(i);
			CBaseObject *pClonedTarget = ctx.FindClone( pTarget );
			if (!pClonedTarget)
				pClonedTarget = pTarget; // If target not cloned, link to original target.

			// Add cloned event.
			if (pClonedTarget)
				AddEntity( pClonedTarget );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::AddEntity( CBaseObject *entity )
{
	assert( entity );

	m_bAreaModified = true;

	StoreUndo( "Add Entity" );
	m_entities.Add( entity );
	UpdateGameArea();
	SW_ON_OBJ_MOD(this);
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::RemoveEntity( int index )
{
	assert( index >= 0 && index < m_entities.GetCount() );
	StoreUndo( "Remove Entity" );

	m_bAreaModified = true;

	if (index < m_entities.GetCount())
	{
		CBaseObject* pObject = m_entities.Get(index);
		
		if (this->IsKindOf(RUNTIME_CLASS(CAITerritoryObject)) && pObject->IsKindOf(RUNTIME_CLASS(CAIWaveObject)))
		{
			GetObjectManager()->FindAndRenameProperty2If("aiwave_Wave", pObject->GetName(), "<None>", "aiterritory_Territory", this->GetName());
		}
		
		m_entities.Remove(pObject);
	}
	UpdateGameArea();
	SW_ON_OBJ_MOD(this);
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CShapeObject::GetEntity( int index )
{
	assert( index >= 0 && index < m_entities.GetCount() );
	return m_entities.Get(index);
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::SetClosed( bool bClosed )
{
	StoreUndo( "Set Closed" );
	mv_closed = bClosed;

	m_bAreaModified = true;

	if (mv_closed)
	{
		UpdateGameArea();
	}
}

//////////////////////////////////////////////////////////////////////////
int CShapeObject::GetAreaId()
{
	return mv_areaId;
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::SelectPoint( int index )
{
	m_selectedPoint = index;
}

void CShapeObject::SetPoint( int index,const Vec3 &pos )
{
	Vec3 p = pos;
	if (m_bForce2D)
	{
		if (!m_points.empty())
			p.z = m_points[0].z; // Keep original Z.
	}

	size_t const nCount	= m_points.size();

	if (index >= 0 && index < nCount)
	{
		for (size_t nIdx = 0; nIdx < nCount; ++nIdx)
		{
			if (nIdx != index) // Don't check against the same point
			{
				float const fDiffX = cry_fabsf(m_points[nIdx].x - p.x);
				float const fDiffY = cry_fabsf(m_points[nIdx].y - p.y);
				float const fDiffZ = cry_fabsf(m_points[nIdx].z - p.z);

				if (fDiffX < SHAPE_POINT_MIN_DISTANCE && fDiffY < SHAPE_POINT_MIN_DISTANCE && fDiffZ < SHAPE_POINT_MIN_DISTANCE)
				{
					//CRY_ASSERT_MESSAGE(fDiffX > SHAPE_POINT_MIN_DISTANCE && fDiffY > SHAPE_POINT_MIN_DISTANCE && fDiffZ > SHAPE_POINT_MIN_DISTANCE, "The point is too close to another point!");
					return;
				}
			}
		}
		
		m_points[index] = p;
	}

	m_bAreaModified = true;
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::ReverseShape()
{
	StoreUndo( "Reverse Shape" );
	std::reverse(m_points.begin(), m_points.end());
	if(mv_closed)
	{
		// Keep the same starting point for closed paths.
		Vec3	tmp(m_points.back());
		for(size_t i = m_points.size() - 1; i > 0; --i)
			m_points[i] = m_points[i - 1];
		m_points[0] = tmp;
	}
	m_bAreaModified = true;
	if (!IsOnlyUpdateOnUnselect())
		UpdateGameArea();
}


//////////////////////////////////////////////////////////////////////////
void CShapeObject::ResetShape()
{
	StoreUndo( "Reset Height Shape" );

	for(size_t i = 0, count=m_points.size(); i < count; i++)
		m_points[i].z = 0;

	m_bAreaModified = true;
	if (!IsOnlyUpdateOnUnselect())
		UpdateGameArea();
}


//////////////////////////////////////////////////////////////////////////
int CShapeObject::GetNearestPoint( const Vec3 &raySrc,const Vec3 &rayDir,float &distance )
{
	int index = -1;
	float minDist = FLT_MAX;
	Vec3 rayLineP1 = raySrc;
	Vec3 rayLineP2 = raySrc + rayDir*RAY_DISTANCE;
	Vec3 intPnt;
	const Matrix34 &wtm = GetWorldTM();
	for (int i = 0; i < m_points.size(); i++)
	{
		float d = PointToLineDistance( rayLineP1,rayLineP2,wtm.TransformPoint(m_points[i]),intPnt );
		if (d < minDist)
		{
			minDist = d;
			index = i;
		}
	}
	distance = minDist;
	return index;
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::GetNearestEdge( const Vec3 &pos,int &p1,int &p2,float &distance,Vec3 &intersectPoint )
{
	p1 = -1;
	p2 = -1;
	float minDist = FLT_MAX;
	Vec3 intPnt;
	
	const Matrix34 &wtm = GetWorldTM();

	for (int i = 0; i < m_points.size(); i++)
	{
		int j = (i < m_points.size()-1) ? i+1 : 0;

		if (!mv_closed && j == 0 && i != 0)
			continue;

		float d = PointToLineDistance( wtm.TransformPoint(m_points[i]),wtm.TransformPoint(m_points[j]),pos,intPnt );
		if (d < minDist)
		{
			minDist = d;
			p1 = i;
			p2 = j;
			intersectPoint = intPnt;
		}
	}
	distance = minDist;
}

//////////////////////////////////////////////////////////////////////////
bool CShapeObject::RayToLineDistance( const Vec3 &rayLineP1,const Vec3 &rayLineP2,const Vec3 &pi,const Vec3 &pj,float &distance,Vec3 &intPnt )
{
	Vec3 pa,pb;
	float ua,ub;
	if (!LineLineIntersect( pi,pj, rayLineP1,rayLineP2, pa,pb,ua,ub ))
		return false;

	// If before ray origin.
	if (ub < 0)
		return false;

	float d = 0;
	if (ua < 0)
		d = PointToLineDistance( rayLineP1,rayLineP2,pi,intPnt );
	else if (ua > 1)
		d = PointToLineDistance( rayLineP1,rayLineP2,pj,intPnt );
	else
	{
		intPnt = rayLineP1 + ub*(rayLineP2-rayLineP1);
		d = (pb-pa).GetLength();
	}
	distance = d;

	return true;
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::GetNearestEdge( const Vec3 &raySrc,const Vec3 &rayDir,int &p1,int &p2,float &distance,Vec3 &intersectPoint )
{
	p1 = -1;
	p2 = -1;
	float minDist = FLT_MAX;
	Vec3 intPnt;
	Vec3 rayLineP1 = raySrc;
	Vec3 rayLineP2 = raySrc + rayDir*RAY_DISTANCE;

	const Matrix34 &wtm = GetWorldTM();

	for (int i = 0; i < m_points.size(); i++)
	{
		int j = (i < m_points.size()-1) ? i+1 : 0;

		if (!mv_closed && j == 0 && i != 0)
			continue;

		Vec3 pi = wtm.TransformPoint(m_points[i]);
		Vec3 pj = wtm.TransformPoint(m_points[j]);

		float d = 0;
		if (!RayToLineDistance( rayLineP1,rayLineP2,pi,pj,d,intPnt ))
			continue;
		
		if (d < minDist)
		{
			minDist = d;
			p1 = i;
			p2 = j;
			intersectPoint = intPnt;
		}
	}
	distance = minDist;
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::UpdateGameArea( bool bRemove )
{
	
	if (bRemove && m_pSoundPropertiesPanel)
	{
		if(m_nSoundPanelID != 0)
		{
			// Internally a var block holds "IVariablePtr", on destroy delete is already called
			m_pSoundPanelVarBlock->Clear(); 

			GetIEditor()->RemoveRollUpPage(ROLLUP_OBJECTS, m_nSoundPanelID);
			m_nSoundPanelID = 0;
		}

		m_pSoundPropertiesPanel	= NULL;
		return;
	}

	if (m_bIgnoreGameUpdate)
		return;

	if (!m_entity)
		return;

	if (GetPointCount() < 2)
		return;

	if (!mv_closed)
		return;

	IEntityAreaProxy *pArea = (IEntityAreaProxy*)m_entity->CreateProxy( ENTITY_PROXY_AREA );
	if (!pArea)
		return;
	
	unsigned int const nPointsCount = GetPointCount();
	if(nPointsCount > 0)
	{
		std::vector<Vec3> points(nPointsCount);
		bool* const pbObstructSound = new bool[nPointsCount];
		for (unsigned int i = 0; i < nPointsCount; ++i)
		{
			points[i]						= GetPoint(i);

			// Here we "unpack" the data! (1 bit*nPointsCount to 1 byte*nPointsCount)
			pbObstructSound[i]	= m_abObstructSound[i];
		}
		
		UpdateSoundPanelParams();
		pArea->SetPoints( &points[0], &pbObstructSound[0], points.size(), GetHeight() );
		delete[] pbObstructSound;

    if(IVoxTerrain * pVoxTerrain = GetIEditor()->Get3DEngine()->GetIVoxTerrain())
      if (strstr(GetName(),"MegaTextureArea"))
    {
      int nShapePartId=0;
      sscanf( GetName(), "MegaTextureArea%d", &nShapePartId);

      PodArray<Vec3> points;
      for (int vtx=0, numVtxs=GetPointCount(); vtx < numVtxs; ++vtx)
      {
        Vec3 vWSPos = GetWorldTM().TransformPoint (GetPoint (vtx));
        points.Add(vWSPos); 
      }

      pVoxTerrain->SetTextureArea(points.GetElements(),points.Count(), nShapePartId);
    }
  }

	pArea->SetProximity( GetWidth() );
	pArea->SetID( mv_areaId );
	pArea->SetGroup( mv_groupId );
	pArea->SetPriority( mv_priority );
	pArea->SetSoundObstructionOnAreaFace( nPointsCount, m_abObstructSound[nPointsCount]);		// Roof
	pArea->SetSoundObstructionOnAreaFace( nPointsCount + 1, m_abObstructSound[nPointsCount+1]);	// Floor

	pArea->ClearEntities();
	for (int i = 0; i < GetEntityCount(); i++)
	{
		CEntityObject *pEntity = (CEntityObject*)GetEntity(i);
		if (pEntity && pEntity->GetIEntity())
		{
			if(gEnv->pEntitySystem->EntitiesUseGUIDs())
				pArea->AddEntity( pEntity->GetIEntity()->GetGuid() );
			else
				pArea->AddEntity( pEntity->GetIEntity()->GetId() );
		}
	}

	m_bAreaModified = false;
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::OnShapeChange( IVariable *var )
{
	m_bAreaModified = true;
	if (!m_bIgnoreGameUpdate)
		UpdateGameArea();
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::OnSoundParamsChange( IVariable *var )
{
	if(!m_bIgnoreGameUpdate)
	{
		if(!mv_displaySoundInfo)
		{
			if(m_nSoundPanelID != 0)
			{
				// Internally a var block holds "IVariablePtr", on destroy delete is already called
				m_pSoundPanelVarBlock->Clear();

				GetIEditor()->RemoveRollUpPage(ROLLUP_OBJECTS, m_nSoundPanelID);
				m_nSoundPanelID					= 0;
				m_pSoundPropertiesPanel = 0;
			}
		}
		else
		{
			if(!m_pSoundPropertiesPanel)
			{
				m_pSoundPropertiesPanel	= new CPropertiesPanel(AfxGetMainWnd());
			}
		}

		UpdateSoundPanelParams();
	}
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::OnPointChange( IVariable *var )
{
	if(m_pSoundPanelVarBlock)
	{
		size_t const nVarCount = m_pSoundPanelVarBlock->GetVarsCount();
		for(size_t i = 0; i < nVarCount; ++i)
		{
			if(m_pSoundPanelVarBlock->GetVariable(i) == var)
			{
				if(m_entity)
				{
					IEntityAreaProxy* const pArea = (IEntityAreaProxy*)m_entity->CreateProxy( ENTITY_PROXY_AREA );
					if(pArea)
					{
						bool bObstructs = false;
						if(!_stricmp(var->GetName(), "Roof"))
						{
							IVariable const* const __restrict pTempRoof = m_pSoundPanelVarBlock->FindVariable("Roof");
							if(pTempRoof)
							{
								pTempRoof->Get(bObstructs);
								pArea->SetSoundObstructionOnAreaFace(nVarCount-2, bObstructs);
								m_abObstructSound[nVarCount-2] = bObstructs;
							}
						}
						else if(!_stricmp(var->GetName(), "Floor"))
						{
							IVariable const* const __restrict	pTempFloor = m_pSoundPanelVarBlock->FindVariable("Floor");
							if(pTempFloor)
							{
								pTempFloor->Get(bObstructs);
								pArea->SetSoundObstructionOnAreaFace(nVarCount-1, bObstructs);
								m_abObstructSound[nVarCount-1] = bObstructs;
							}
						}
						else
						{
							var->Get(bObstructs);
							pArea->SetSoundObstructionOnAreaFace(i, bObstructs);
							m_abObstructSound[i] = bObstructs;
						}
					}
				}
				break;
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::UpdateSoundPanelParams()
{
	if(!m_rollupMultyId && mv_displaySoundInfo && m_pSoundPropertiesPanel)
	{
		if(!m_nSoundPanelID)
			m_nSoundPanelID	= GetIEditor()->AddRollUpPage(ROLLUP_OBJECTS, "Sound Obstruction", m_pSoundPropertiesPanel);

		m_pSoundPropertiesPanel->DeleteVars();

		if(m_nSoundPanelID)
		{
			if(m_pSoundPanelVarBlock)
			{
				if(!m_pSoundPanelVarBlock->IsEmpty())
					m_pSoundPanelVarBlock->Clear();

				if(!m_abObstructSound.empty())
				{
					// If the shape hasn't got a height subtract 2 for roof and floor
					size_t const nVarCount = m_abObstructSound.size()-((mv_height>0.0f)?0:2);
					for(size_t i = 0; i < nVarCount; ++i)
					{
						CVariable<bool>* const pvTemp = new CVariable<bool>;
						pvTemp->Set(m_abObstructSound[i]);
						pvTemp->AddOnSetCallback(functor(*this, &CShapeObject::OnPointChange));

						stack_string cTemp;
						if(i == nVarCount-2 && mv_height > 0.0f)
							cTemp.Format("Roof");
						else if(i == nVarCount-1 && mv_height > 0.0f)
							cTemp.Format("Floor");
						else
							cTemp.Format("Side:%d", i+1);

						m_pSoundPanelVarBlock->AddVariable(pvTemp, cTemp);
					}
				}

				m_pSoundPropertiesPanel->AddVars(m_pSoundPanelVarBlock);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::SpawnEntity()
{
	if (!m_entityClass.IsEmpty())
	{
		__super::SpawnEntity();
		UpdateGameArea();
	}
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::OnEvent( ObjectEvent event )
{
	switch (event)
	{
	case EVENT_ALIGN_TOGRID:
		{
			AlignToGrid();
		}
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::PostLoad( CObjectArchive &ar )
{
	UpdateGameArea();


	//////////////////////////////////////////////////////////////////////////
	// Load flow graph after loading of everything.
	// Taken from CEntityObject::PostLoad(CObjectArchive& ar) [10/24/2011 evgeny]
	if (XmlNodeRef graphNode = ar.node->findChild("FlowGraph"))
	{
		if (!m_pFlowGraph)
		{
			SetFlowGraph(GetIEditor()->GetFlowGraphManager()->CreateGraphForEntity(this));
		}

		if (m_pFlowGraph && m_pFlowGraph->GetIFlowGraph())
		{
			m_pFlowGraph->Serialize(graphNode, true, &ar);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
bool CShapeObject::HitTestRect( HitContext &hc )
{
	AABB box;
	// Retrieve world space bound box.
	GetBoundBox( box );

	// Project all edges to viewport.
	const Matrix34 &wtm = GetWorldTM();
	for (int i = 0; i < m_points.size(); i++)
	{
		int j = (i < m_points.size()-1) ? i+1 : 0;
		if (!mv_closed && j == 0 && i != 0)
			continue;

		Vec3 p0 = wtm.TransformPoint(m_points[i]);
		Vec3 p1 = wtm.TransformPoint(m_points[j]);

		CPoint pnt0 = hc.view->WorldToView( p0 );
		CPoint pnt1 = hc.view->WorldToView( p1 );

		// See if pnt0 to pnt1 line intersects with rectangle.
		// check see if one point is inside rect and other outside, or both inside.
		bool in0 = hc.rect.PtInRect(pnt0);
		bool in1 = hc.rect.PtInRect(pnt0);
		if ((in0 && in1) || (in0 && !in1) || (!in0 && in1))
		{
			hc.object = this;
			return true;
		}
	}

	return false;

/*

	// transform all 8 vertices into world space
	CPoint p[8] = 
	{ 
		hc.view->WorldToView(Vec3d(box.min.x,box.min.y,box.min.z)),
			hc.view->WorldToView(Vec3d(box.min.x,box.max.y,box.min.z)),
			hc.view->WorldToView(Vec3d(box.max.x,box.min.y,box.min.z)),
			hc.view->WorldToView(Vec3d(box.max.x,box.max.y,box.min.z)),
			hc.view->WorldToView(Vec3d(box.min.x,box.min.y,box.max.z)),
			hc.view->WorldToView(Vec3d(box.min.x,box.max.y,box.max.z)),
			hc.view->WorldToView(Vec3d(box.max.x,box.min.y,box.max.z)),
			hc.view->WorldToView(Vec3d(box.max.x,box.max.y,box.max.z))
	};

	CRect objrc,temprc;

	objrc.left = 10000;
	objrc.top = 10000;
	objrc.right = -10000;
	objrc.bottom = -10000;
	// find new min/max values
	for(int i=0; i<8; i++)
	{
		objrc.left = min(objrc.left,p[i].x);
		objrc.right = max(objrc.right,p[i].x);
		objrc.top = min(objrc.top,p[i].y);
		objrc.bottom = max(objrc.bottom,p[i].y);
	}
	if (objrc.IsRectEmpty())
	{
		// Make objrc at least of size 1.
		objrc.bottom += 1;
		objrc.right += 1;
	}
	if (temprc.IntersectRect( objrc,hc.rect ))
	{
		hc.object = this;
		return true;
	}
	return false;
	*/
}

//////////////////////////////////////////////////////////////////////////
void CShapeObject::AlignToGrid()
{
	CViewport *view = GetIEditor()->GetActiveView();
	if (!view)
		return;

	CUndo undo( "Align Shape To Grid" );
	StoreUndo( "Align Shape To Grid" );

	Matrix34 wtm = GetWorldTM();
	Matrix34 invTM = wtm.GetInverted();
	for (int i = 0; i < GetPointCount(); i++)
	{
		Vec3 pnt = wtm.TransformPoint( GetPoint(i) );
		pnt = view->SnapToGrid( pnt );
		SetPoint( i,invTM.TransformPoint(pnt) );
	}
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
// CLightShapeObject
//////////////////////////////////////////////////////////////////////////
CLightShapeObject::CLightShapeObject() : CShapeObject()
{
	m_pBrush = NULL;
	m_bDisplayFilledWhenSelected = false;
	m_entityClass = "LightShape";
	m_lightId = GUID_NULL;
	SetColor( RGB(0,0,200) );
}

//////////////////////////////////////////////////////////////////////////
void CLightShapeObject::InitVariables()
{
	AddVariable(m_strGeomFile,"geom_file",NULL,IVariable::DT_FILE);
	AddVariable(mv_height,"Height",functor(*this,&CLightShapeObject::OnShapeChange));

	m_strGeomFile->SetFlags(IVariable::UI_INVISIBLE);

	// avoid adding inherited shape object's variables
	CEntityObject::InitVariables();
}

//////////////////////////////////////////////////////////////////////////
void CLightShapeObject::EndCreation()
{
	UpdateCgf();
}

//////////////////////////////////////////////////////////////////////////
void CLightShapeObject::RemovePoint( int index )
{
	CShapeObject::RemovePoint(index);
	UpdateCgf();
}

//////////////////////////////////////////////////////////////////////////
void CLightShapeObject::EndPointModify()
{
	UpdateCgf();
}


//////////////////////////////////////////////////////////////////////////
void CLightShapeObject::OnShapeChange( IVariable *var )
{
	CShapeObject::OnShapeChange(var);
	UpdateCgf();
}

//////////////////////////////////////////////////////////////////////////
void CLightShapeObject::CreateBrush()
{
	m_pBrush = new SBrush;
	if ( m_pBrush )
	{
		m_pBrush->CreateSimpleMesh(m_points,GetHeight());
		IStatObj * pObj = NULL;
		if ( m_pBrush->GetIStatObj(pObj) )
		{
      CString gamefolder = Path::GetGameFolder();
      CString levelpath = CString(GetIEditor()->GetGameEngine()->GetLevelPath()).MakeLower();
      CString rootfolder = levelpath.Left(levelpath.Find(gamefolder));
      CString levelfolder = Path::GetFile(levelpath.MakeLower());
      CString filename = rootfolder + gamefolder + "\\Objects\\lightshapes\\" + levelfolder + "\\" + GetName() + ".cgf";
			CFileUtil::CreateDirectory(Path::GetPath(CString(filename)));
			if ( pObj->SaveToCGF(filename) )
			{
				filename = Path::GetRelativePath(filename,true);
				Path::ConvertBackSlashToSlash(filename);
				m_strGeomFile->Set(filename);

				CVarBlock * pVarBlock = GetProperties();
				if ( !pVarBlock )
					return;

				IVariable * pVar = pVarBlock->FindVariable("geom_file",true);
				if ( !pVar )
					return;

				pVar->Set(filename);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CLightShapeObject::UpdateCgf(bool bExport)
{
	if ( m_points.size() > 2 )
	{
		if ( m_pBrush )
		{
			delete m_pBrush;
			m_pBrush = NULL;
		}
		
		CreateBrush();

    if ( !bExport )
    {
		  CBaseObject *pObject = FindObject(m_lightId);
		  if (pObject && pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		  {
			  CEntityObject * target = (CEntityObject*)pObject;
			  target->Reload();
		  }
    }
	}
}

//////////////////////////////////////////////////////////////////////////
XmlNodeRef CLightShapeObject::Export( const CString &levelPath,XmlNodeRef &xmlNode )
{
	UpdateCgf(true);
	return CShapeObject::Export(levelPath, xmlNode);
}

//////////////////////////////////////////////////////////////////////////
void CLightShapeObject::EntityLinked( const CString &name,GUID targetEntityId )
{
	CEntityObject *target = 0;
	if (targetEntityId != GUID_NULL)
	{
		CBaseObject *pObject = FindObject(targetEntityId);
		if (pObject && pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			m_lightId = targetEntityId;
			target = (CEntityObject*)pObject;
		}
  	
	  if ( !target )
		  return;

    CVarBlock * pVarBlock = NULL;
    IVariable * pVar = NULL;

    CString type = pObject->GetTypeDescription();
    CString filename;

    if ( stricmp(type,"Light") == 0 )
    {
	    pVarBlock = target->GetProperties();
	    if ( !pVarBlock )
		    return;
    }
    else if ( stricmp(type,"DestroyableLight") == 0 || stricmp(type,"RigidBodyLight") == 0 )
    {
      pVarBlock = target->GetProperties2();
      if ( !pVarBlock )
        return;
    }
    else
    {
      return;
    }

    pVar = pVarBlock->FindVariable("file_deferred_clip_geom",true);
    if ( !pVar )
      return;

    m_strGeomFile->Get(filename);
    pVar->Set(filename);

	  pVarBlock = GetProperties();
	  if ( !pVarBlock )
		  return;

	  pVar = pVarBlock->FindVariable("geom_file",true);
	  if ( !pVar )
		  return;

	  pVar->Set(filename);
  }
}

int CLightShapeObject::InsertPoint( int index,const Vec3 &point, bool const bModifying )
{
	if (GetPointCount() >= GetMaxPoints())
		return GetPointCount()-1;
	int newindex;
	StoreUndo( "Insert Point" );

	m_bAreaModified = true;

	if (index < 0 || index >= m_points.size())
	{
		m_points.push_back( point );
		newindex = m_points.size()-1;

		if(m_abObstructSound.empty())
		{
			// This shape is being created therefore add the point plus roof and floor positions
			m_abObstructSound.resize(3);
		}
		else
		{
			// Update the "bools list", insert just before the "Roof" position
			m_abObstructSound.insert(m_abObstructSound.end()-2, false);
		}
	}
	else
	{
		m_points.insert( m_points.begin()+index,point );
		newindex = index;

		// Also update the "bools list"
		m_abObstructSound.insert(m_abObstructSound.begin()+index, false);
	}
	SetPoint( newindex,point );
	CalcBBox();
	return newindex;
}

void CLightShapeObject::SetPoint( int index,const Vec3 &pos )
{
	Vec3 p = pos;
	if (m_bForce2D)
	{
		if (!m_points.empty())
			p.z = m_points[0].z; // Keep original Z.
	}
	if (index >= 0 && index < m_points.size())
	{
		m_points[index] = p;
	}
	m_bAreaModified = true;
	if (!IsOnlyUpdateOnUnselect())
		UpdateGameArea();
}

void CLightShapeObject::PostClone( CBaseObject *pFromObject,CObjectCloneContext &ctx )
{
	__super::PostClone(pFromObject,ctx);
	UpdateCgf();
}

void CLightShapeObject::BeginEditParams( IEditor *ie,int flags )
{
	CEntityObject::BeginEditParams( ie,flags );

	if (!m_panel)
	{
		m_panel = new CShapePanel;
		m_panel->Create( CShapePanel::IDD );
		m_rollupId = ie->AddRollUpPage( ROLLUP_OBJECTS,"Shape Parameters",m_panel );
	}
	if (m_panel)
		m_panel->SetShape( this );
}

//////////////////////////////////////////////////////////////////////////
void CLightShapeObject::StoreUndo( const char *UndoDescription,bool minimal )
{
  CBaseObject::StoreUndo(UndoDescription,minimal);
  UpdateCgf();
}
//////////////////////////////////////////////////////////////////////////
// CAIForbiddenAreaObject
//////////////////////////////////////////////////////////////////////////
CAIForbiddenAreaObject::CAIForbiddenAreaObject()
{
	m_bDisplayFilledWhenSelected = true;
	SetColor( RGB(255,0,0) );
}

//////////////////////////////////////////////////////////////////////////
void CAIForbiddenAreaObject::UpdateGameArea( bool bRemove )
{
	if (m_bIgnoreGameUpdate)
		return;
	if (!IsCreateGameObjects())
		return;

	IAISystem *ai = GetIEditor()->GetSystem()->GetAISystem();
	if (ai)
	{
		if (m_updateSucceed && !m_lastGameArea.IsEmpty())
			GetNavigation()->DeleteNavigationShape( m_lastGameArea );
		if (bRemove)
			return;

		if (GetNavigation()->DoesNavigationShapeExists(GetPrefabPrependedName(), AREATYPE_FORBIDDEN))
		{
			//gEnv->pSystem->GetILog()->LogError("Forbidden Area", "Shape '%s' already exists in AIsystem, please rename the shape.", GetName() );
			m_updateSucceed = false;
			return;
		}

		std::vector<Vec3> worldPts;
		const Matrix34 &wtm = GetWorldTM();
		for (int i = 0; i < GetPointCount(); i++)
			worldPts.push_back(wtm.TransformPoint(GetPoint(i)));

		m_lastGameArea = GetPrefabPrependedName();
		if (!worldPts.empty())
    {
      SNavigationShapeParams params(m_lastGameArea, AREATYPE_FORBIDDEN, false, mv_closed, &worldPts[0], worldPts.size());
			m_updateSucceed = GetNavigation()->CreateNavigationShape(params);
    }
	}
	m_bAreaModified = false;
}

//////////////////////////////////////////////////////////////////////////
// CAIForbiddenBoundaryObject
//////////////////////////////////////////////////////////////////////////
CAIForbiddenBoundaryObject::CAIForbiddenBoundaryObject()
{
	m_bDisplayFilledWhenSelected = true;
	SetColor( RGB(255,255,0) );
}

//////////////////////////////////////////////////////////////////////////
void CAIForbiddenBoundaryObject::UpdateGameArea( bool bRemove )
{
	if (m_bIgnoreGameUpdate)
		return;
	if (!IsCreateGameObjects())
		return;

	IAISystem *ai = GetIEditor()->GetSystem()->GetAISystem();
	if (ai)
	{
		if (m_updateSucceed && !m_lastGameArea.IsEmpty())
			GetNavigation()->DeleteNavigationShape( m_lastGameArea );
		if (bRemove)
			return;

		if (GetNavigation()->DoesNavigationShapeExists(GetPrefabPrependedName(), AREATYPE_FORBIDDENBOUNDARY))
		{
			//gEnv->pSystem->GetILog()->LogError("Forbidden Boundary", "Shape '%s' already exists in AIsystem, please rename the shape.", GetName() );
			m_updateSucceed = false;
			return;
		}

		std::vector<Vec3> worldPts;
		const Matrix34 &wtm = GetWorldTM();
		for (int i = 0; i < GetPointCount(); i++)
			worldPts.push_back(wtm.TransformPoint(GetPoint(i)));

		m_lastGameArea = GetPrefabPrependedName();
		if (!worldPts.empty())
    {
      SNavigationShapeParams params(m_lastGameArea, AREATYPE_FORBIDDENBOUNDARY, false, mv_closed, &worldPts[0], worldPts.size());
			m_updateSucceed = GetNavigation()->CreateNavigationShape(params);
    }
	}
	m_bAreaModified = false;
}

//////////////////////////////////////////////////////////////////////////
// CAIPathObject.
//////////////////////////////////////////////////////////////////////////
CAIPathObject::CAIPathObject()
{
	SetColor( RGB(180,180,180) );
  SetClosed(false);
  m_bRoad = false;
	m_bValidatePath = false;

	m_navType.AddEnumItem("Unset", (int)IAISystem::NAV_UNSET);
	m_navType.AddEnumItem("Triangular", (int)IAISystem::NAV_TRIANGULAR);
	m_navType.AddEnumItem("Waypoint Human", (int)IAISystem::NAV_WAYPOINT_HUMAN);
	m_navType.AddEnumItem("Waypoint 3D Surface", (int)IAISystem::NAV_WAYPOINT_3DSURFACE);
	m_navType.AddEnumItem("Flight", (int)IAISystem::NAV_FLIGHT);
	m_navType.AddEnumItem("Volume", (int)IAISystem::NAV_VOLUME);
	m_navType.AddEnumItem("Road", (int)IAISystem::NAV_ROAD);
	m_navType.AddEnumItem("SmartObject", (int)IAISystem::NAV_SMARTOBJECT);
	m_navType.AddEnumItem("Free 2D", (int)IAISystem::NAV_FREE_2D);

	m_navType = (int)IAISystem::NAV_UNSET;
	m_anchorType = "";
	
	AddVariable( m_bRoad,"Road",functor(*this,&CAIPathObject::OnShapeChange) );
	AddVariable( m_navType,"PathNavType",functor(*this,&CAIPathObject::OnShapeChange) );
	AddVariable( m_anchorType,"AnchorType",functor(*this,&CAIPathObject::OnShapeChange), IVariable::DT_AI_ANCHOR );
	AddVariable( m_bValidatePath,"ValidatePath" ); //,functor(*this,&CAIPathObject::OnShapeChange) );

	m_bDisplayFilledWhenSelected = false;
}

void CAIPathObject::UpdateGameArea( bool bRemove )
{
	if (m_bIgnoreGameUpdate)
		return;
	if (!IsCreateGameObjects())
		return;

	IAISystem *ai = GetIEditor()->GetSystem()->GetAISystem();
	if (ai)
	{
		if (m_updateSucceed && !m_lastGameArea.IsEmpty())
		{
			GetNavigation()->DeleteNavigationShape( m_lastGameArea );
			ai->DeleteNavigationShape( m_lastGameArea );
		}
		if (bRemove)
			return;

		if (GetNavigation()->DoesNavigationShapeExists(GetPrefabPrependedName(), AREATYPE_PATH, m_bRoad))
		{
			//gEnv->pSystem->GetILog()->LogError("AI Path", "Path '%s' already exists in AIsystem, please rename the path.", GetName() );
			m_updateSucceed = false;
			return;
		}

		std::vector<Vec3> worldPts;
		const Matrix34 &wtm = GetWorldTM();
		for (int i = 0; i < GetPointCount(); i++)
			worldPts.push_back(wtm.TransformPoint(GetPoint(i)));

		m_lastGameArea = GetPrefabPrependedName();
		if (!worldPts.empty())
		{
			CAIManager *pAIMgr = GetIEditor()->GetAI();
			CString	anchorType(m_anchorType);
			int	type = pAIMgr->AnchorActionToId(anchorType);
			SNavigationShapeParams params(m_lastGameArea, AREATYPE_PATH, m_bRoad, mv_closed, &worldPts[0], worldPts.size(), 0, m_navType, type);
			m_updateSucceed = GetNavigation()->CreateNavigationShape(params);
			ai->CreateNavigationShape(params);
		}
	}
	m_bAreaModified = false;
}

void CAIPathObject::DrawSphere(DisplayContext &dc, const Vec3& p0, float r, int n)
{
	// Note: The Aux Render already supports drawing spheres, but they appear
	// shaded when rendered and there is no cylinder rendering available.	
	// This function is here just for the purpose to make the rendering of
	// the validatedsegments more consistent.
	Vec3	a0, a1, b0, b1;
	for(int j = 0; j < n/2; j++ )
	{
		float theta1 = j * gf_PI2 / n - gf_PI/2;
		float theta2 = (j + 1) * gf_PI2 / n - gf_PI/2;

		for(int i = 0; i <= n; i++ )
		{
			float theta3 = i * gf_PI2 / n;

			a0.x = p0.x + r * cosf(theta2) * cosf(theta3);
			a0.y = p0.y + r * sinf(theta2);
			a0.z = p0.z + r * cosf(theta2) * sinf(theta3);

			b0.x = p0.x + r * cosf(theta1) * cosf(theta3);
			b0.y = p0.y + r * sinf(theta1);
			b0.z = p0.z + r * cosf(theta1) * sinf(theta3);

			if(i > 0)
				dc.DrawQuad(a0, b0, b1, a1);

			a1 = a0;
			b1 = b0;
		}
	}
}

void CAIPathObject::DrawCylinder(DisplayContext &dc, const Vec3& p0, const Vec3& p1, float rad, int n)
{
	Vec3	dir(p1 - p0);
	Vec3	a = dir.GetOrthogonal();
	Vec3	b = dir.Cross(a);
	a.NormalizeSafe();
	b.NormalizeSafe();

	for(int i = 0; i < n; i++)
	{
		float	a0 = ((float)i / (float)n) * gf_PI2;
		float	a1 = ((float)(i+1) / (float)n) * gf_PI2;
		Vec3	n0 = sinf(a0) * rad * a + cosf(a0) * rad * b;
		Vec3	n1 = sinf(a1) * rad * a + cosf(a1) * rad * b;

		dc.DrawQuad(p0 + n0, p1 + n0, p1 + n1, p0 + n1);
	}
}

bool CAIPathObject::IsSegmentValid(const Vec3& p0, const Vec3& p1, float rad)
{
	AABB	box;
	box.Reset();
	box.Add(p0);
	box.Add(p1);
	box.min -= Vec3(rad, rad, rad);
	box.max += Vec3(rad, rad, rad);

	IPhysicalEntity **entities;
	unsigned nEntities = gEnv->pPhysicalWorld->GetEntitiesInBox(box.min, box.max, entities, ent_static|ent_ignore_noncolliding);

	primitives::sphere spherePrim;
	spherePrim.center = p0;
	spherePrim.r = rad;
	Vec3	dir(p1 - p0);

	unsigned hitCount = 0;
	ray_hit hit;
	IPhysicalWorld* pPhysics = gEnv->pPhysicalWorld;

	for (unsigned iEntity = 0 ; iEntity < nEntities ; ++iEntity)
	{
		IPhysicalEntity *pEntity = entities[iEntity];
		if (pPhysics->CollideEntityWithPrimitive(pEntity, spherePrim.type, &spherePrim, dir, &hit))
		{
			return false;
			break;
		}
	}
	return true;
}

void CAIPathObject::Display( DisplayContext &dc )
{
	// Display path validation
	if(m_bValidatePath && IsSelected())
	{
		dc.DepthWriteOff();
		int num = m_points.size();
		if(!m_bRoad && m_navType == (int)IAISystem::NAV_VOLUME && num >= 3)
		{
			const float rad = mv_width;
			const Matrix34 &wtm = GetWorldTM();

			if(!mv_closed)
			{
				Vec3 p0 = wtm.TransformPoint(m_points.front());
				float	viewScale = dc.view->GetScreenScaleFactor(p0);
				if(viewScale < 0.01f) viewScale = 0.01f;
				unsigned n = CLAMP((unsigned)(300.0f / viewScale), 4, 20);
				DrawSphere(dc, p0, rad, n);
			}

			for (int i = 0; i < num; i++)
			{
				int j = (i < m_points.size()-1) ? i+1 : 0;
				if (!mv_closed && j == 0 && i != 0)
					continue;

				Vec3 p0 = wtm.TransformPoint(m_points[i]);
				Vec3 p1 = wtm.TransformPoint(m_points[j]);

				float	viewScale = max(dc.view->GetScreenScaleFactor(p0), dc.view->GetScreenScaleFactor(p1));
				if(viewScale < 0.01f) viewScale = 0.01f;
				unsigned n = CLAMP((unsigned)(300.0f / viewScale), 4, 20);

				if(IsSegmentValid(p0, p1, rad))
					dc.SetColor(1,1,1,0.25f);
				else
					dc.SetColor(1,0.5f,0,0.25f);

				DrawCylinder(dc, p0, p1, rad, n);
				DrawSphere(dc, p1, rad, n);
			}
		}
		dc.DepthWriteOn();
	}

	// Display the direction of the path
	if(m_points.size() > 1)
	{
		const Matrix34 &wtm = GetWorldTM();
		Vec3	p0 = wtm.TransformPoint(m_points[0]);
		Vec3	p1 = wtm.TransformPoint(m_points[1]);
		if(IsSelected())
			dc.SetColor(dc.GetSelectedColor());
		else if(IsHighlighted())
			dc.SetColor( RGB(255,120,0) );
		else
			dc.SetColor(GetColor());
		Vec3	pt((p1 - p0)/2);
		if(pt.GetLength() > 1.0f)
			pt.SetLength(1.0f);
		dc.DrawArrow(p0, p0 + pt);

		float l = 0;
		for (int i=1; i< m_points.size(); ++i)
			l += (m_points[i-1]-m_points[i]).len();

		CString msg("");
		msg.Format(" Length: %.2fm", l);
		dc.DrawTextLabel(wtm.GetTranslation(), 1.2f, msg, true);
	}

	CShapeObject::Display(dc);
}

int CAIPathObject::InsertPoint(int index, const Vec3& point, bool const bModifying)
{
	int result = __super::InsertPoint(index, point, bModifying);

	UpdateGameArea(false);

	return result;
}

void CAIPathObject::SetPoint(int index, const Vec3& pos)
{
	__super::SetPoint(index, pos);

	UpdateGameArea(false);
}


void CAIPathObject::RemovePoint(int index)
{
	__super::RemovePoint(index);

	UpdateGameArea(false);
}

//////////////////////////////////////////////////////////////////////////
// CAIShapeObject.
//////////////////////////////////////////////////////////////////////////
CAIShapeObject::CAIShapeObject()
{
	SetColor( RGB(30,65,120) );
	SetClosed(true);

	m_anchorType = "";
	AddVariable( m_anchorType,"AnchorType",functor(*this,&CAIShapeObject::OnShapeChange), IVariable::DT_AI_ANCHOR );

	m_lightLevel.AddEnumItem("Default", AILL_NONE);
	m_lightLevel.AddEnumItem("Light", AILL_LIGHT);
	m_lightLevel.AddEnumItem("Medium", AILL_MEDIUM);
	m_lightLevel.AddEnumItem("Dark", AILL_DARK);
	m_lightLevel.AddEnumItem("SuperDark", AILL_SUPERDARK);
	m_lightLevel = AILL_NONE;
	AddVariable( m_lightLevel,"LightLevel",functor(*this,&CAIShapeObject::OnShapeChange) );

	m_bDisplayFilledWhenSelected = false;
}

void CAIShapeObject::UpdateGameArea( bool bRemove )
{
	if (m_bIgnoreGameUpdate)
		return;
	if (!IsCreateGameObjects())
		return;

	IAISystem *ai = GetIEditor()->GetSystem()->GetAISystem();
	if (ai)
	{
		if (m_updateSucceed && !m_lastGameArea.IsEmpty())
			GetNavigation()->DeleteNavigationShape( m_lastGameArea );
		if (bRemove)
			return;

		if (GetNavigation()->DoesNavigationShapeExists(GetPrefabPrependedName(), AREATYPE_GENERIC))
		{
			//gEnv->pSystem->GetILog()->LogError("AI Shape", "Shape '%s' already exists in AIsystem, please rename the shape.", GetName() );
			m_updateSucceed = false;
			return;
		}

		std::vector<Vec3> worldPts;
		const Matrix34 &wtm = GetWorldTM();
		for (int i = 0; i < GetPointCount(); i++)
			worldPts.push_back(wtm.TransformPoint(GetPoint(i)));

		m_lastGameArea = GetPrefabPrependedName();
		if (!worldPts.empty())
		{
			CAIManager *pAIMgr = GetIEditor()->GetAI();
			CString	anchorType(m_anchorType);
			int	type = pAIMgr->AnchorActionToId(anchorType);
			SNavigationShapeParams params(m_lastGameArea, AREATYPE_GENERIC, false, mv_closed, &worldPts[0], worldPts.size(),
				GetHeight(), 0, type, (EAILightLevel)(int)m_lightLevel);
			m_updateSucceed = GetNavigation()->CreateNavigationShape(params);
		}
	}
	m_bAreaModified = false;
}

//////////////////////////////////////////////////////////////////////////
// CAINavigationModifierObject
//////////////////////////////////////////////////////////////////////////
CAINavigationModifierObject::CAINavigationModifierObject()
{
	m_navType.AddEnumItem("Human Waypoint", NMT_WAYPOINTHUMAN);
	m_navType.AddEnumItem("Volume", NMT_VOLUME);
	m_navType.AddEnumItem("Flight", NMT_FLIGHT);
	m_navType.AddEnumItem("Water", NMT_WATER);
	m_navType.AddEnumItem("3D Surface Waypoint ", NMT_WAYPOINT_3DSURFACE);
	m_navType.AddEnumItem("Extra Link Cost", NMT_EXTRA_NAV_COST);
	m_navType.AddEnumItem("Free 2D", NMT_FREE_2D);
	m_navType.AddEnumItem("Triangulation", NMT_TRIANGULATION);
	m_navType = NMT_WAYPOINTHUMAN;

	m_waypointConnections.AddEnumItem("Designer", WPCON_DESIGNER_NONE);
	m_waypointConnections.AddEnumItem("Designer-Dynamic", WPCON_DESIGNER_PARTIAL);
	m_waypointConnections.AddEnumItem("Auto", WPCON_AUTO_NONE);
	m_waypointConnections.AddEnumItem("Auto-Dynamic", WPCON_AUTO_PARTIAL);
	m_waypointConnections = WPCON_DESIGNER_NONE;
	m_oldWaypointConnections = m_waypointConnections;

	m_lightLevel.AddEnumItem("Default", AILL_NONE);
	m_lightLevel.AddEnumItem("Light", AILL_LIGHT);
	m_lightLevel.AddEnumItem("Medium", AILL_MEDIUM);
	m_lightLevel.AddEnumItem("Dark", AILL_DARK);
	m_lightLevel.AddEnumItem("SuperDark", AILL_SUPERDARK);
	m_lightLevel = AILL_NONE;

	m_bDisplayFilledWhenSelected = true;
	SetColor( RGB(24,128,231) );

	m_bCalculate3DNav = true;
	m_bCrittersOnly = false;

	m_f3DNavVolumeRadius = 10.0f;

	m_fNodeAutoConnectDistance = 8.0f;

	m_fExtraLinkCostFactor = 0.0f;
	m_fExtraLinkCostFactor.SetLimits(-1.0f, 100.0f);

	m_bVehiclesInHumanNav = false;
	m_vEnabled=true;
	m_vExclusion=false;

	AddVariable( m_navType,"NavType",functor(*this,&CAINavigationModifierObject::OnShapeTypeChange));
	AddVariable( m_waypointConnections,"WaypointConnections",functor(*this,&CAINavigationModifierObject::OnShapeChange));
	AddVariable( m_lightLevel,"LightLevel",functor(*this,&CAINavigationModifierObject::OnShapeChange));
	AddVariable( m_bCalculate3DNav,"Calculate3DNav",functor(*this,&CAINavigationModifierObject::OnShapeChange));
	AddVariable( m_bCrittersOnly,"CrittersOnly",functor(*this,&CAINavigationModifierObject::OnShapeChange));
	AddVariable( m_f3DNavVolumeRadius,"ThreeDNavVolumeRadius",functor(*this,&CAINavigationModifierObject::OnShapeChange));	
	AddVariable( m_fNodeAutoConnectDistance,"NodeAutoConnectDistance",functor(*this,&CAINavigationModifierObject::OnShapeChange));
	AddVariable( m_fExtraLinkCostFactor,"ExtraLinkCostFactor",functor(*this,&CAINavigationModifierObject::OnShapeChange));
	AddVariable( m_bVehiclesInHumanNav,"VehiclesInHumanNav",functor(*this,&CAINavigationModifierObject::OnShapeChange));
	AddVariable( m_vEnabled,"Enabled",functor(*this,&CAINavigationModifierObject::OnShapeChange));
	AddVariable( m_vExclusion,"Exclude",functor(*this,&CAINavigationModifierObject::OnShapeChange));
	
	AddVariable( m_vPFPropertiesList, "AgentTypeList", functor(*this, &CAINavigationModifierObject::OnShapeChange), IVariable::DT_AI_PFPROPERTIESLIST);
	int nFlags = m_vPFPropertiesList.GetFlags();
	m_vPFPropertiesList.SetFlags(nFlags | IVariable::UI_INVISIBLE);
}

void CAINavigationModifierObject::Serialize( CObjectArchive &ar )
{
	XmlNodeRef xmlNode = ar.node;
	if (ar.bLoading)
	{
    bool oldAutoGenerateNav = false;
		xmlNode->getAttr("AutoGenerateNav", oldAutoGenerateNav);
    if (oldAutoGenerateNav)
      m_waypointConnections = WPCON_AUTO_PARTIAL;
  }
  __super::Serialize(ar);

	if (ar.bLoading)
  {
    bool useAreaIDForNavType = true;
    xmlNode->getAttr("UseAreaIDForNavType", useAreaIDForNavType);
    if (useAreaIDForNavType)
    {
      m_navType = GetAreaId();
      mv_areaId = 0;
    }
  }
  else
  {
    bool useAreaIDForNavType = false;
    xmlNode->setAttr("UseAreaIDForNavType", useAreaIDForNavType);
  }

}


void CAINavigationModifierObject::UpdateGameArea( bool bRemove )
{
	if (m_bIgnoreGameUpdate)
		return;
	if (!IsCreateGameObjects())
		return;

	IAISystem *ai = GetIEditor()->GetSystem()->GetAISystem();
	if (ai)
	{
		if (m_updateSucceed && !m_lastGameArea.IsEmpty())
			GetNavigation()->DeleteNavigationShape( m_lastGameArea );
		if (bRemove)
			return;

		if (GetNavigation()->DoesNavigationShapeExists(GetPrefabPrependedName(), AREATYPE_NAVIGATIONMODIFIER))
		{
			//gEnv->pSystem->GetILog()->LogError("Navigation Modifier", "Shape '%s' already exists in AIsystem, please rename the shape.", GetName() );
			m_updateSucceed = false;
			return;
		}

		std::vector<Vec3> worldPts;
		const Matrix34 &wtm = GetWorldTM();
		for (int i = 0; i < GetPointCount(); i++)
			worldPts.push_back(wtm.TransformPoint(GetPoint(i)));

		m_lastGameArea = GetPrefabPrependedName();
    if (m_waypointConnections < 0)
      m_waypointConnections = 0;
    else if (m_waypointConnections > WPCON_MAXVALUE)
      m_waypointConnections = WPCON_MAXVALUE;
		if (!worldPts.empty())
    {
      SNavigationShapeParams params(m_lastGameArea, AREATYPE_NAVIGATIONMODIFIER, false, mv_closed, &worldPts[0], worldPts.size(), 
				GetHeight(), m_navType, 0, (EAILightLevel)(int)m_lightLevel,
				m_fNodeAutoConnectDistance, (EWaypointConnections) (int) m_waypointConnections, m_bVehiclesInHumanNav,
        m_bCalculate3DNav, m_bCrittersOnly, m_f3DNavVolumeRadius, m_fExtraLinkCostFactor);
      params.szPFPropertiesList = CString(m_vPFPropertiesList).GetString();
			params.flyAgentWidth			= mv_agentWidth;
			params.flyAgentHeight			= mv_agentHeight;

			params.flightNavData.flyAgentWidth = mv_agentWidth;
			params.flightNavData.flyAgentHeight = mv_agentHeight;
			params.flightNavData.voxelOffsetX = mv_VoxelOffsetX;
			params.flightNavData.voxelOffsetY = mv_VoxelOffsetY;

			m_updateSucceed = GetNavigation()->CreateNavigationShape(params);
		  if (m_oldWaypointConnections != m_waypointConnections)
			  GetNavigation()->SetUseAutoNavigation(GetName(), (EWaypointConnections) (int) m_waypointConnections);
		  m_oldWaypointConnections = m_waypointConnections;
    }
	}
	m_bAreaModified = false;
}


void CAINavigationModifierObject::OnShapeTypeChange( IVariable *var )
{
	int nEnumValue(0);

	var->Get(nEnumValue);

	// All switch cases were removed -- just comment out for now.
	// switch (nEnumValue)
	{
		// default:
			{
				HideAllVariables();

				int nCurrentFlags(0);

				nCurrentFlags=m_navType.GetFlags();
				m_navType.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=m_waypointConnections.GetFlags();
				m_waypointConnections.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=m_fNodeAutoConnectDistance.GetFlags();
				m_fNodeAutoConnectDistance.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				if (nEnumValue == NMT_WAYPOINTHUMAN)
				{
					nCurrentFlags=m_bCrittersOnly.GetFlags();
					m_bCrittersOnly.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);
				}

				nCurrentFlags=m_bCalculate3DNav.GetFlags();
				m_bCalculate3DNav.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=m_f3DNavVolumeRadius.GetFlags();
				m_f3DNavVolumeRadius.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=m_fExtraLinkCostFactor.GetFlags();
				m_fExtraLinkCostFactor.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=m_bVehiclesInHumanNav.GetFlags();
				m_bVehiclesInHumanNav.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=m_lightLevel.GetFlags();
				m_lightLevel.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=mv_width.GetFlags();
				mv_width.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=mv_height.GetFlags();
				mv_height.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=mv_areaId.GetFlags();
				mv_areaId.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=mv_groupId.GetFlags();
				mv_groupId.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=mv_priority.GetFlags();
				mv_priority.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=mv_closed.GetFlags();
				mv_closed.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=mv_displayFilled.GetFlags();
				mv_displayFilled.SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);

				nCurrentFlags=m_vPFPropertiesList.GetFlags();
				m_vPFPropertiesList.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE); // <--BUG?? Why | ?

				// Show all sound variables
				if(m_pSoundPanelVarBlock)
				{
					size_t const nVarCount = (size_t )m_pSoundPanelVarBlock->GetVarsCount();
					for(size_t i = 0; i < nVarCount; ++i)
					{
						IVariable* const pTemp = m_pSoundPanelVarBlock->GetVariable(i);
						if(pTemp)
						{
							nCurrentFlags = pTemp->GetFlags();
							pTemp->SetFlags(nCurrentFlags&~IVariable::UI_INVISIBLE);
						}
					}
				}

				RefreshUIPanel();
			}
		// break;
	}

	
	OnShapeChange(var);
}

void CAINavigationModifierObject::HideAllVariables()
{
	int nCurrentFlags(0);

	nCurrentFlags=m_navType.GetFlags();
	m_navType.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=m_waypointConnections.GetFlags();
	m_waypointConnections.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=m_fNodeAutoConnectDistance.GetFlags();
	m_fNodeAutoConnectDistance.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=m_bCalculate3DNav.GetFlags();
	m_bCalculate3DNav.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=m_bCrittersOnly.GetFlags();
	m_bCrittersOnly.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=m_f3DNavVolumeRadius.GetFlags();
	m_f3DNavVolumeRadius.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=m_fExtraLinkCostFactor.GetFlags();
	m_fExtraLinkCostFactor.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=m_bVehiclesInHumanNav.GetFlags();
	m_bVehiclesInHumanNav.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=m_lightLevel.GetFlags();
	m_lightLevel.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=mv_width.GetFlags();
	mv_width.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=mv_height.GetFlags();
	mv_height.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=mv_areaId.GetFlags();
	mv_areaId.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=mv_groupId.GetFlags();
	mv_groupId.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=mv_priority.GetFlags();
	mv_priority.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=mv_closed.GetFlags();
	mv_closed.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=mv_displayFilled.GetFlags();
	mv_displayFilled.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);
	
	nCurrentFlags=m_vPFPropertiesList.GetFlags();
	m_vPFPropertiesList.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=mv_agentHeight.GetFlags();
	mv_agentHeight.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=mv_agentWidth.GetFlags();
	mv_agentWidth.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=mv_renderVoxelGrid.GetFlags();
	mv_renderVoxelGrid.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=mv_VoxelOffsetX.GetFlags();
	mv_VoxelOffsetX.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	nCurrentFlags=mv_VoxelOffsetY.GetFlags();
	mv_VoxelOffsetY.SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);

	// Hide all sound variables
	if(m_pSoundPanelVarBlock)
	{
		size_t const nVarCount = (size_t )m_pSoundPanelVarBlock->GetVarsCount();
		for(size_t i = 0; i < nVarCount; ++i)
		{
			IVariable* const pTemp = m_pSoundPanelVarBlock->GetVariable(i);
			if(pTemp)
			{
				nCurrentFlags = pTemp->GetFlags();
				pTemp->SetFlags(nCurrentFlags|IVariable::UI_INVISIBLE);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
// CAIOclussionPlaneObject
//////////////////////////////////////////////////////////////////////////
CAIOcclusionPlaneObject::CAIOcclusionPlaneObject()
{
	m_bDisplayFilledWhenSelected = true;
	SetColor( RGB(24,90,231) );
	m_bForce2D = true;
	mv_closed = true;
	mv_displayFilled = true;
}

void CAIOcclusionPlaneObject::UpdateGameArea( bool bRemove )
{
	if (m_bIgnoreGameUpdate)
		return;
	if (!IsCreateGameObjects())
		return;

	IAISystem *ai = GetIEditor()->GetSystem()->GetAISystem();
	if (ai)
	{
		if (m_updateSucceed && !m_lastGameArea.IsEmpty())
		{
			GetNavigation()->DeleteNavigationShape( m_lastGameArea );
			ai->DeleteNavigationShape( m_lastGameArea );
		}
		if (bRemove)
			return;

		if (GetNavigation()->DoesNavigationShapeExists(GetPrefabPrependedName(), AREATYPE_OCCLUSION_PLANE))
		{
			//gEnv->pSystem->GetILog()->LogError("OcclusionPlane", "Shape '%s' already exists in AIsystem, please rename the shape.", GetName() );
			m_updateSucceed = false;
			return;
		}

		std::vector<Vec3> worldPts;
		const Matrix34 &wtm = GetWorldTM();
		for (int i = 0; i < GetPointCount(); i++)
			worldPts.push_back(wtm.TransformPoint(GetPoint(i)));

		m_lastGameArea = GetPrefabPrependedName();
		if (!worldPts.empty())
    {
      SNavigationShapeParams params(m_lastGameArea,AREATYPE_OCCLUSION_PLANE, false, mv_closed, &worldPts[0], worldPts.size(), GetHeight());
			m_updateSucceed = GetNavigation()->CreateNavigationShape(params);
			ai->CreateNavigationShape(params);
    }
	}
	m_bAreaModified = false;
}


//////////////////////////////////////////////////////////////////////////
// CAIPerceptionModifier
//////////////////////////////////////////////////////////////////////////
CAIPerceptionModifierObject::CAIPerceptionModifierObject()
{
	m_bDisplayFilledWhenSelected = true;
	SetColor( RGB(24,90,231) );
	m_bForce2D = false;
	mv_closed = false;
	mv_displayFilled = true;
	m_bPerVertexHeight = false;
	m_fReductionPerMetre = 0.0f;
	m_fReductionMax = 1.0f;
	//AddVariable( m_fReductionPerMetre, "ReductionPerMetre", NULL, IVariable::DT_PERCENT );
	//AddVariable( m_fReductionMax, "ReductionMax", NULL, IVariable::DT_PERCENT );
}

void CAIPerceptionModifierObject::InitVariables()
{
	AddVariable( m_fReductionPerMetre, "ReductionPerMetre", functor(*this,&CAIPerceptionModifierObject::OnShapeChange), IVariable::DT_PERCENT );
	AddVariable( m_fReductionMax, "ReductionMax", functor(*this,&CAIPerceptionModifierObject::OnShapeChange), IVariable::DT_PERCENT );
	AddVariable( mv_height,"Height",functor(*this,&CAIPerceptionModifierObject::OnShapeChange) );
	AddVariable( mv_closed,"Closed",functor(*this,&CAIPerceptionModifierObject::OnShapeChange) );
	AddVariable( mv_displayFilled,"DisplayFilled" );
	//__super::InitVariables();
}

void CAIPerceptionModifierObject::UpdateGameArea( bool bRemove )
{
	if (m_bIgnoreGameUpdate)
		return;
	if (!IsCreateGameObjects())
		return;

	IAISystem *pAI = GetIEditor()->GetSystem()->GetAISystem();
	if (pAI)
	{
		if (!m_lastGameArea.IsEmpty())
			GetNavigation()->DeleteNavigationShape( m_lastGameArea );
		if (bRemove)
			return;

		std::vector<Vec3> worldPts;
		const Matrix34 &wtm = GetWorldTM();
		for (int i = 0; i < GetPointCount(); i++)
			worldPts.push_back(wtm.TransformPoint(GetPoint(i)));

		m_lastGameArea = GetPrefabPrependedName();
		if (!worldPts.empty())
		{
			SNavigationShapeParams params(m_lastGameArea,AREATYPE_PERCEPTION_MODIFIER, false, mv_closed, &worldPts[0], worldPts.size(), GetHeight());
			params.fReductionPerMetre = m_fReductionPerMetre;
			params.fReductionMax = m_fReductionMax;
			GetNavigation()->CreateNavigationShape(params);
		}
	}
	m_bAreaModified = false;
}



//////////////////////////////////////////////////////////////////////////
// CTerritory
//////////////////////////////////////////////////////////////////////////
CAITerritoryObject::CAITerritoryObject()
{
	m_entityClass = "AITerritory";

	m_bDisplayFilledWhenSelected = true;
	SetColor( RGB(24,90,231) );
	m_bForce2D = false;
	mv_closed = true;
	mv_displayFilled = false;
	m_bPerVertexHeight = false;
}

void CAITerritoryObject::BeginEditParams(IEditor* ie, int flags)
{
	__super::BeginEditParams(ie, flags);
	
	if (!CEntityObject::m_panel)
	{
		CEntityObject::m_panel = new CAITerritoryPanel(AfxGetMainWnd());
		CEntityObject::m_panel->Create(CAITerritoryPanel::IDD, AfxGetMainWnd());
		CEntityObject::m_rollupId = AddUIPage(CString("Entity: ") + m_entityClass, CEntityObject::m_panel);
	}
	if (CEntityObject::m_panel && CEntityObject::m_panel->m_hWnd)
	{
		CEntityObject::m_panel->SetEntity(this);
		static_cast<CAITerritoryPanel*>(CEntityObject::m_panel)->UpdateAssignedAIsPanel();
	}

	CEntityObject::BeginEditParams( ie,flags ); // At the end add entity dialogs.
}

void CAITerritoryObject::BeginEditMultiSelParams(bool bAllOfSameType)
{
	CBaseObject::BeginEditMultiSelParams(bAllOfSameType);

	if (!CEntityObject::m_panel)
	{
		CEntityObject::m_panel = new CAITerritoryPanel(AfxGetMainWnd());
		CEntityObject::m_panel->Create(CAITerritoryPanel::IDD, AfxGetMainWnd());
		CEntityObject::m_rollupId = AddUIPage(CString(m_entityClass) + " and other Entities", CEntityObject::m_panel);
	}
	if (CEntityObject::m_panel && CEntityObject::m_panel->m_hWnd)
	{
		static_cast<CAITerritoryPanel*>(CEntityObject::m_panel)->UpdateAssignedAIsPanel();
	}
	
	if (!bAllOfSameType)
		return;

	if (!m_panelMulty)
	{
		m_panelMulty = new CShapeMultySelPanel;
		m_rollupMultyId = GetIEditor()->AddRollUpPage(ROLLUP_OBJECTS, "Multi Shape Operation", m_panelMulty);
	}
}

void CAITerritoryObject::EndEditMultiSelParams()
{
	if (m_rollupId != 0)
	{
		RemoveUIPage( m_rollupId );
	}
	m_rollupId = 0;
	m_panel = 0;

	if (m_rollupMultyId != 0)
	{
		GetIEditor()->RemoveRollUpPage(ROLLUP_OBJECTS, m_rollupMultyId);
		CalcBBox();
	}
	m_rollupMultyId = 0;
	m_panelMulty = 0;

	CBaseObject::EndEditMultiSelParams();
}

void CAITerritoryObject::SetName(const CString& newName)
{
	CString oldName = GetName();

	__super::SetName(newName);
	
	GetIEditor()->GetAI()->GetAISystem()->Reset(IAISystem::RESET_INTERNAL);

	GetObjectManager()->FindAndRenameProperty2("aiterritory_Territory", oldName, newName);
}

void CAITerritoryObject::InitVariables()
{
	AddVariable( mv_height,"Height",functor(*this,&CAITerritoryObject::OnShapeChange) );
	AddVariable( mv_displayFilled,"DisplayFilled" );
}

void CAITerritoryObject::UpdateGameArea( bool bRemove )
{
	if (m_bIgnoreGameUpdate)
		return;
	if (!IsCreateGameObjects())
		return;

	IAISystem *pAI = GetIEditor()->GetSystem()->GetAISystem();
	if (pAI)
	{
		if (!m_lastGameArea.IsEmpty())
			GetNavigation()->DeleteNavigationShape( m_lastGameArea );
		if (bRemove)
			return;

		std::vector<Vec3> worldPts;
		const Matrix34 &wtm = GetWorldTM();
		for (int i = 0; i < GetPointCount(); i++)
			worldPts.push_back(wtm.TransformPoint(GetPoint(i)));

		m_lastGameArea = GetPrefabPrependedName();
		if (!worldPts.empty())
		{
			SNavigationShapeParams params(m_lastGameArea, AREATYPE_GENERIC, false, mv_closed, &worldPts[0], worldPts.size(), GetHeight(), 0, AIANCHOR_COMBAT_TERRITORY);
			GetNavigation()->CreateNavigationShape(params);
		}
	}
	m_bAreaModified = false;
}

void CAITerritoryObject::GetLinkedWaves(std::vector<CAIWaveObject*>& result)
{
	result.clear();
	
	for (int i = 0, n = GetEntityCount(); i < n; ++i)
	{
		CBaseObject* pBaseObject = GetEntity(i);
		if (pBaseObject->IsKindOf(RUNTIME_CLASS(CAIWaveObject)))
		{
			result.push_back(static_cast<CAIWaveObject*>(pBaseObject));
		}
	}
}
//////////////////////////////////////////////////////////////////////////
// Navigation Area Object
//////////////////////////////////////////////////////////////////////////
CNavigationAreaObject::CNavigationAreaObject()
{
	m_bDisplayFilledWhenSelected = true;

	m_bForce2D = false;
	mv_closed = true;
	mv_displayFilled = true;
	m_bPerVertexHeight = true;

	m_volume = NavigationVolumeID();

	m_entityClass = "MNMNavigationArea";
}

CNavigationAreaObject::~CNavigationAreaObject()
{
	mv_agentTypes.DeleteAllChilds();
}

void CNavigationAreaObject::BeginEditParams(IEditor* pEditor, int flags)
{
	CBaseObject::BeginEditParams(pEditor, flags);

	if (!m_panel)
	{
		m_panel = new CNavigationAreaPanel(AfxGetMainWnd());
		m_panel->Create(CNavigationAreaPanel::IDD, AfxGetMainWnd());
		m_rollupId = pEditor->AddRollUpPage(ROLLUP_OBJECTS,
			"Edit Options", m_panel);
	}

	if (m_panel)
		m_panel->SetShape(this);
}

void CNavigationAreaObject::Display(DisplayContext& dc)
{
	if (IsSelected() || GetIEditor()->GetAI()->GetShowNavigationAreasState())
	{
		SetColor(mv_exclusion ? RGB(200, 0, 0) : RGB(0, 126, 255));

		float lineWidth = dc.GetLineWidth();
		dc.SetLineWidth(8.0f);
		__super::Display(dc);
		dc.SetLineWidth(lineWidth);
	}
}

void CNavigationAreaObject::PostLoad( CObjectArchive &ar )
{
	if (IAISystem* aiSystem = GetIEditor()->GetSystem()->GetAISystem())
	{
		if(INavigationSystem* aiNavigation = aiSystem->GetNavigationSystem())
		{
			aiNavigation->RegisterArea(GetName().GetString());
		}
	}

	__super::PostLoad(ar);
}

void CNavigationAreaObject::Done()
{
	__super::Done();

	if (IAISystem* aiSystem = GetIEditor()->GetSystem()->GetAISystem())
	{
		if(INavigationSystem* aiNavigation = aiSystem->GetNavigationSystem())
		{
			aiNavigation->UnRegisterArea(GetName().GetString());
		}
	}
}

void CNavigationAreaObject::InitVariables()
{
	mv_height = 3;
	mv_exclusion = false;
	mv_displayFilled = false;

	AddVariable(mv_height, "Height", functor(*this, &CNavigationAreaObject::OnShapeChange));
	AddVariable(mv_exclusion, "Exclusion", functor(*this, &CNavigationAreaObject::OnShapeTypeChange));
	AddVariable(mv_displayFilled, "DisplayFilled");

	CAIManager* manager = GetIEditor()->GetAI();
	size_t agentTypeCount = manager->GetNavigationAgentTypeCount();

	if (agentTypeCount)
	{
		mv_agentTypeVars.resize(agentTypeCount);
		AddVariable(mv_agentTypes, "AffectedAgentTypes", "Affected Agent Types");

		for (size_t i = 0; i < agentTypeCount; ++i)
		{
			const char* name = manager->GetNavigationAgentTypeName(i);
			AddVariable(mv_agentTypes, mv_agentTypeVars[i], name, name,
				functor(*this, &CNavigationAreaObject::OnShapeAgentTypeChange));
		}
	}
}

void CNavigationAreaObject::OnShapeTypeChange( IVariable *var )
{
	if (m_bIgnoreGameUpdate)
		return;

	UpdateMeshes();
	ApplyExclusion(mv_exclusion);
}

void CNavigationAreaObject::OnShapeAgentTypeChange( IVariable *var )
{
	if (m_bIgnoreGameUpdate)
		return;

	UpdateMeshes();
	ApplyExclusion(mv_exclusion);
}

void CNavigationAreaObject::OnNavigationEvent(const INavigationSystem::ENavigationEvent event)
{
	switch(event)
	{
		case INavigationSystem::MeshReloaded: 
			RelinkWithMesh(); 
			break;
		case INavigationSystem::MeshReloadedAfterExporting: 
			RelinkWithMesh(false);
			break;
		case INavigationSystem::NavigationCleared:
			RelinkWithMesh();
			break;
		default:
			assert(0);
			break;
	}
}

void CNavigationAreaObject::UpdateMeshes()
{
	CAIManager* manager = GetIEditor()->GetAI();
	const size_t agentTypeCount = manager->GetNavigationAgentTypeCount();

	IAISystem* aiSystem = GetIEditor()->GetSystem()->GetAISystem();

	if (mv_exclusion)
	{
		for (size_t i = 0; i < m_meshes.size(); ++i)
		{
			if (const NavigationMeshID meshID = m_meshes[i])
				aiSystem->GetNavigationSystem()->DestroyMesh(meshID);
		}

		m_meshes.clear();
	}
	else
	{
		m_meshes.resize(agentTypeCount);

		for (size_t i = 0; i < agentTypeCount; ++i)
		{
			NavigationMeshID meshID = m_meshes[i];

			if (mv_agentTypeVars[i] && !meshID)
			{
				NavigationAgentTypeID agentTypeID = aiSystem->GetNavigationSystem()->GetAgentTypeID(i);

				INavigationSystem::CreateMeshParams params; // TODO: expose at least the tile size
				meshID = aiSystem->GetNavigationSystem()->CreateMesh(GetName().GetString(), agentTypeID, params);
				aiSystem->GetNavigationSystem()->SetMeshBoundaryVolume(meshID, m_volume);

				AABB aabb;
				GetBoundBox(aabb);
				aiSystem->GetNavigationSystem()->QueueMeshUpdate(meshID, aabb);

				m_meshes[i] = meshID;
			}
			else if (!mv_agentTypeVars[i] && meshID)
			{
				aiSystem->GetNavigationSystem()->DestroyMesh(meshID);
				m_meshes[i] = NavigationMeshID();
			}
		}
	}
}

void CNavigationAreaObject::ApplyExclusion(bool apply)
{
	CAIManager* manager = GetIEditor()->GetAI();
	size_t agentTypeCount = manager->GetNavigationAgentTypeCount();

	IAISystem* aiSystem = GetIEditor()->GetSystem()->GetAISystem();

	std::vector<NavigationAgentTypeID> affectedAgentTypes;
	
	if (apply)
	{
		affectedAgentTypes.reserve(agentTypeCount);

		for (size_t i = 0; i < agentTypeCount; ++i)
		{
			if (mv_agentTypeVars[i])
			{
				NavigationAgentTypeID agentTypeID = aiSystem->GetNavigationSystem()->GetAgentTypeID(i);
				affectedAgentTypes.push_back(agentTypeID);
			}
		}
	}

	if (affectedAgentTypes.empty())
		aiSystem->GetNavigationSystem()->SetExclusionVolume(0, 0, m_volume);
	else
		aiSystem->GetNavigationSystem()->SetExclusionVolume(&affectedAgentTypes[0], affectedAgentTypes.size(), m_volume);
}

void CNavigationAreaObject::RelinkWithMesh(bool bUpdateGameArea)
{
	IAISystem* pAISystem = GetIEditor()->GetSystem()->GetAISystem();

	if(!pAISystem)
		return;

	INavigationSystem* pAINavigation = pAISystem->GetNavigationSystem();
	const char* navigationAreaName = GetName().GetString();
	m_volume = pAINavigation->GetAreaId(navigationAreaName);
	if(!mv_exclusion)
	{
		CAIManager* manager = GetIEditor()->GetAI();
		const size_t agentTypeCount = manager->GetNavigationAgentTypeCount();
		m_meshes.resize(agentTypeCount);
		for (size_t i = 0; i < agentTypeCount; ++i)
		{
			NavigationAgentTypeID agentTypeID = pAINavigation->GetAgentTypeID(i);
			m_meshes[i] = pAINavigation->GetMeshID(navigationAreaName,agentTypeID);
		}
	}	

	/*
	  FR: We need to update the game area even in the case that after having read
	  the data from the binary file the volumewas not recreated.
	  This happens when there was no mesh associated to the actual volume.
	*/
	if(bUpdateGameArea || !pAINavigation->ValidateVolume(m_volume) )
		UpdateGameArea(false);
}

void CNavigationAreaObject::CreateVolume(Vec3* points, size_t pointsSize, NavigationVolumeID requestedID /* = NavigationVolumeID */)
{
	if (IAISystem* aiSystem = GetIEditor()->GetSystem()->GetAISystem())
	{
		if(INavigationSystem* aiNavigation = aiSystem->GetNavigationSystem())
		{
			const char* navigationAreaName = GetName().GetString();
			m_volume = aiNavigation->CreateVolume(points, pointsSize, mv_height, requestedID);
			aiNavigation->RegisterListener(this, navigationAreaName);
			if(requestedID == NavigationVolumeID())
				aiNavigation->SetAreaId(navigationAreaName, m_volume);
		}
	}
}

void CNavigationAreaObject::DestroyVolume()
{
	if (IAISystem* aiSystem = GetIEditor()->GetSystem()->GetAISystem())
	{
		if(INavigationSystem* aiNavigation = aiSystem->GetNavigationSystem())
		{
			aiNavigation->DestroyVolume(m_volume);
			aiNavigation->UnRegisterListener(this);
			m_volume = NavigationVolumeID();
		}
	}
}

void CNavigationAreaObject::UpdateGameArea(bool remove)
{
	if (m_bIgnoreGameUpdate)
		return;

	if (!IsCreateGameObjects())
		return;
	
	if (IAISystem* aiSystem = GetIEditor()->GetSystem()->GetAISystem())
	{
		INavigationSystem* aiNavigation = aiSystem->GetNavigationSystem();

		if (remove)
		{
			for (size_t i = 0; i < m_meshes.size(); ++i)
			{
				if (m_meshes[i])
					aiNavigation->DestroyMesh(m_meshes[i]);
			}

			m_meshes.clear();

			if (m_volume)
			{
				DestroyVolume();
			}

			return;
		}

		const Matrix34& worldTM = GetWorldTM();
		const size_t pointCount = GetPointCount();
		
		if (pointCount > 2)
		{
			std::vector<Vec3> points;
			points.reserve(pointCount);

			for (size_t i = 0; i < pointCount; ++i)
				points.push_back(worldTM.TransformPoint(GetPoint(i)));

			// The volume could be set but if the binary data didn't exist the volume
			// was not correctly recreated
			if (!m_volume || !aiNavigation->ValidateVolume(m_volume))
			{
				CreateVolume(&points[0], points.size(),m_volume);
			}
			else
				aiNavigation->SetVolume(m_volume, &points[0], points.size(), mv_height);

			UpdateMeshes();
		}
		else if (m_volume)
		{
			DestroyVolume();
			UpdateMeshes();
		}

		ApplyExclusion(mv_exclusion);
	}

	m_lastGameArea = GetName();
	m_bAreaModified = false;
}

void CNavigationAreaObject::SetName( const CString &name )
{
	__super::SetName(name);

	if (IAISystem* aiSystem = GetIEditor()->GetSystem()->GetAISystem())
	{
		INavigationSystem* pNavigationSystem = aiSystem->GetNavigationSystem();
		pNavigationSystem->UpdateAreaNameForId(m_volume, name);
		for (size_t i = 0; i < m_meshes.size(); ++i)
		{
			if (m_meshes[i])
				pNavigationSystem->SetMeshName(m_meshes[i], name.GetString());
		}
	}
}

void CNavigationAreaObject::RemovePoint( int index )
{
	__super::RemovePoint(index);

	UpdateGameArea(false);
}

int CNavigationAreaObject::InsertPoint( int index,const Vec3 &point, bool const bModifying )
{
	int result = __super::InsertPoint(index, Vec3(point.x, point.y, 0.0f), bModifying);

	UpdateGameArea(false);

	return result;
}

void CNavigationAreaObject::SetPoint( int index,const Vec3 &pos )
{
	__super::SetPoint(index, Vec3(pos.x, pos.y, 0.0f));
	
	UpdateGameArea(false);
}

void CNavigationAreaObject::ChangeColor(COLORREF color)
{
	SetModified(false);
}
